http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cd513bf3/docs/design/3_Extensions.adoc ---------------------------------------------------------------------- diff --git a/docs/design/3_Extensions.adoc b/docs/design/3_Extensions.adoc deleted file mode 100644 index db949ac..0000000 --- a/docs/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! --------------------------------------------- - -
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cd513bf3/docs/design/4_ImplementationCore.adoc ---------------------------------------------------------------------- diff --git a/docs/design/4_ImplementationCore.adoc b/docs/design/4_ImplementationCore.adoc deleted file mode 100644 index 2233002..0000000 --- a/docs/design/4_ImplementationCore.adoc +++ /dev/null @@ -1,47 +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 Implementation Concepts -Tamaya comes with an implementation module that implements the Tamaya API. The API itself does only provide the API for configuration usage. -Especially it does not define how the current runtime +Environment+ is mapped to a +Configuration+. This the gaop that should be solved -by the core library. - -The high level packages give you a good overview of the functionality provided: - -* +org.apache.tamaya.core+ is the root package of the Tamaya core implementation. -* +org.apache.tamaya.core.config+ provides - - - -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. http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cd513bf3/docs/pom.xml ---------------------------------------------------------------------- diff --git a/docs/pom.xml b/docs/pom.xml index 965ff76..5e02db1 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -62,7 +62,7 @@ under the License. <embedAssets>true</embedAssets> <imagesDir>${project.build.sourceDirectory}/images/</imagesDir> <sourceDirectory>.</sourceDirectory> - <sourceDocumentName>Design.adoc</sourceDocumentName> + <sourceDocumentName>src/main/asciidoc/design/Design.adoc</sourceDocumentName> </configuration> </execution> <execution> http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cd513bf3/docs/src/main/asciidoc/PossibleContributions.adoc ---------------------------------------------------------------------- diff --git a/docs/src/main/asciidoc/PossibleContributions.adoc b/docs/src/main/asciidoc/PossibleContributions.adoc new file mode 100644 index 0000000..a0040d6 --- /dev/null +++ b/docs/src/main/asciidoc/PossibleContributions.adoc @@ -0,0 +1,388 @@ +Apache Tamaya - Possible Tasks +============================== +:name: Tamaya +:rootpackage: org.apache.tamaya +:title: Apache Tamaya +:revnumber: 0.1-SNAPSHOT +:revremark: Draft +:revdate: October 2014 +:longversion: {revnumber} ({revremark}) {revdate} +:authorinitials: ATR +:author: Anatole Tresch +:email: <atsti...@gmail.com> +:source-highlighter: coderay +:website: http://tamaya.apache.org/ +:iconsdir: {imagesdir}/icons +:toc: +:toc-placement: manual +:icons: +:encoding: UTF-8 +:numbered: + +''' + +<<< + +-> add image : : https://raw.githubusercontent.com/JavaConfig/config-api/master/src/main/asciidoc/images/javaconfig.jpg[] + +toc::[] + +<<< +:numbered!: +----------------------------------------------------------- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +----------------------------------------------------------- + +:numbered: + +<<< + +== Introduction + +== What is Tamaya + +{name} is the Apache standard for flexible and powerful configuration. Objective is to provide flavors for +Java SE, ME as well as to ship with powerful features for Java EE and Cloud Solutions. All functions provided +is build on top of a small but very powerful, flexible and extendible API. This API is implemented by a core implementation, +which then can be extended or adapted for use in different runtime scenarios, such as SE, ME, EE, Spring, OSGI +and more. Similarly additional modules may be provided that help also existing solution to be plugged into +{name}, so you can start right away using {name} without having to rebuild/change your existing application. + +== Main Features of {name} + +The main features of {name} currently are + +* *A simple key/value store*, named +PropertyProvider+ (simply called "provider"). +* Simple *built-in meta-data model*, that allows to easily attach metadata to a provider or single property keys. +* Support for *different configuration formats*. +* Support for *different configuration locations*, including remote locations. +* Powerful and flexible *options to combine providers to new composite providers* using different combination policies. +* The *Configuration model* adds additional features: +** *Type Adapters* allow to convert configuration into any required target type. +** *Built-in support* for all basic Java types. +* *Contextual, hierarchical and multi-layered environment model*. The model is rather simple, but nevertheless + powerful enough to map complex layered runtime environments such as Java EE or multi-tenant SaaS solutions. +* *Configurable System Properties* allow to tweak system properties to behave contextually. +* *Templates* provide a type safe configuration mechanism where an annotated interface is implemented by the + configuration system, providing data from an underlying configuration. +* *Configuration Injection* allows configured values to be directly injected into the bean's configured. +* *Loading Policies* allow to control how configuration changes are reflected on the configured beans. +* *Configuration Operators and Queries* can be used to easily implement advanced features such as *Views, + Security Constraints and Filters*. +* Provider and configuration changes can be observed by registering *PropertyChangeListeners*. +* Configurations are *versioned*, so remote pull scenarios can be implemented very efficiently. +* The system supports *multiple configurations* identified by name. +* The configuration system provides a powerful management console for reading and updating of configuration. +* The system also supports distributed configuration scenarios by leveraging existing solutions, such as Memcached, + Hazelcast or Zookeper. +* The system is built on "Java 8 features*. +* A *Java 7 Backport* is provided. + +=== Purpose of this Document + +The document should help to organize people and ideas around the Apache Tamaya Library. It list possible features, +ideas and tasks that need to be done. Everybody can have a look at and see, where hos contribution and capabilities +would fit best. + +== Basics + +=== Styles, Logo + +The project requires + +* a good Apache styled logo and +* CSS styles as needed, +* an initial web page, +* a twitter account +* ... + +=== Infrastructure + +We should setup all needed infrastructure +* code repos +* project modules (including module sites) +* coding and documentation guidelines +* automatic builds (CI), included automatic coverage and sonar quality checks. +* a docker image or appliance, with everything setup, so contributors can easily + start contributing... +* ... + +== Main Features + +=== Metadata Model + +Currently +MetaInfo+ models metadata as a separate constuct. It has been shown that this leads to more complex +handling when creating composites and makes the API overall more complex. The idea is to model metadata as simple +key/value pairs, that are part of the provider/configuration data as well, but handled separately. Metadata hereby +is identified by a starting '_' character in its key. For example refer to the following configuration properties: + +[source,listing] +.Basic Properties +---------------------------------------------------------------- +a.b.Foo=foo +a.b.Bar=bar +a.AnyOther=whatelse +Something=none +---------------------------------------------------------------- + +Now we can model meta-data as follows: + +[source,listing] +.Metadata Properties +---------------------------------------------------------------- +[a.b].info=An area info +[a.b.Foo].auth=role1,role2 +[a.b.Foo].encrypt=PGP +[a.b.Foo].sensitive=true +[].info=This is a test configuration example. +---------------------------------------------------------------- + +The above would model the following: + +* The area +a.b+ has the meta property +info+. +* The entry +a.b.Foo+ has three meta properties +auth,encrypt+ and +sensitive+. These could be interpreted by a security + view and used to encrypt the values returned by the configuration instance, if not the current user has one of the + specified roles. +* The last meta data defines an attribute +info+ for the whole provider/configuration (the root area). + +Given that the overall entries would be as follows: + +[source,listing] +.Full Properties with Meta Properties +---------------------------------------------------------------- +[a.b].info=An area info +a.b.Foo=foo +[a.b.Foo].auth=role1,role2 +[a.b.Foo].encrypt=PGP +[a.b.Foo].sensitive=true +a.b.Bar=bar +[].info=This is a test configuration example. +a.AnyOther=whatelse +Something=none +---------------------------------------------------------------- + +The current +MetaInfo+ class could be adapted, so it is reading data from the underlying configuration/provider, +instead of its own datastructure. This would make a later mapping of configuration and its metadata into DB table, JSON +etc, much more easier. +The providers on the other side may suppress any metadata from ordinary output, such +as +toString()+, Similarly accessing metadata using the official config API (+get, getOrDefault, getAreas+ etc) +should be disabled. The +MetaInfoBuilder+ must probably as well adapted or redesigned. + +=== Collection Support + +Add a key/value based model for mapping collections such as sets, maps, list. Implement according adapters. +In combination with the metadata model above this could be something like: + +[source,listing] +.Collection Support +---------------------------------------------------------------- +mySet=[a,b,c,d,e\,e,f] +[mySet].type=set +#optional define the implementation class +[mySet].class=java.util.TreeSet + +myList=[a,b,c,d,e\,e,f] +[myList].type=list +#optional define the implementation class +[myList].class=java.util.ArrayList + +myMap=[a:aa,b:bb,c:cc,d:dd,e:e\,e,f:ff] +[myMap].type=map +#optional define the implementation class +[myMap].class=java.util.TreeMap + +#Finally we could also add support for non String based types +myTypedSet=[1,2,3,4.5,6,7.10.123] +[myTypedSet].contentClass=java.lang.Double +myTypedList=[CHF 10.20, EUR 12.20, BTC 0.002] +[myTypedList].contentType=org.javamoney.moneta.FastMoney +myTypedMap=[CHF:CHF 10.20, EUR:EUR 12.20, BTC:BTC 0.002] +[myTypedMap].contentTypes=javax.money.CurrencyUnit,javax.money.MonetaryAmount +---------------------------------------------------------------- + + +=== Management Service + +A JMX/Restful API should be designed and built that exposes configuration information. Access should be secured, e.g. +using OAuth or other security mechasnisms. + +=== Management Client + +A nice web-based client to manage configuration data would be nice as well. This also includes a UI for creating new +configurations. + +=== Mapping Configuration to a Database + +A flexible mechanism should be implemented that allows the use of databases (SQL/JPA as well as non-SQL) for +storing/retreiving/managing configuration: + +* JPA, Hibernate +* MongoDB +* ... + +=== Integration with OSGI + +Examples are to be created and tested, where OSGI is used as the basic runtime platform, e.g. Apache Felix, but as well +others. + +=== Integration with Jigsaw + +Once Jigsaw is mature and in a usable (still early) stage, examples are to be created and tested, where OSGI is used as +the basic runtime platform, e.g. Apache Felix, but as well others. + +== Distributed/Remote Configuration Support + +=== Configuration Server + +A configuration server should be implemented that provides access to configurations and triggers updates to registered +clients (push). Similarly a poull model should be supported, where clients can asl for the current version id of a certain +configuration and reload it if necessary. + +=== Configuration Distribution Policies + +Different configuration distribution policies should be defined any implemented, e.g. distributed cache, restful services, +web services, EJB/RMI calls, asynchronous queues, publish/subsribe models, ... + +=== Dynamic Service Lookup + +Configuration Servers and Clients should bea ble to locate each other in different ways: + +* with fixed configured IPs, or IP ranges +* using a dynamic service location protocol like +** SLP +** Distributed Maps/Datagrids +** Apache Zookeeper + +=== Configuration Client + +A subset of the API would be created that exposes only a well defined subset, of exactly one configuration targeted +to a certain instance, VM or whatever. The client should be connectable to a server in different ways (see configuration +distributiont policies). + +=== Preferences Support + +Write a +PreferencesFactory+ for +java.util.preferences+. + +== Third Party Integration + +=== Integration with Deltaspike Config + +Integration with Deltaspike Config should be implemented and discussed with Deltaspike guys. + +=== Integration with Spring + +A {name} module should be created that allows Spring to be used either as client or configuration provider. + +=== Integration with Jetty + +A {name} module should be created that allows a Jetty instance to be deployed and started that is (completely) +configured based on configuration server. + +=== Integration with Tomcat + +A {name} module should be created that allows a Tomcat instance to be deployed and started that is (completely) +configured based on configuration server. + +=== Configuration of Java EE + +In the Java EE area there would be several options: + +=== Configuration of Application Servers (administrative resources) + +It should be possible to start a application server instance remotely and configure all administrative resources and the +deployments based on the configuration service, server to be considered maybe + +* Wildfly +* IBM +* Weblogic +* Glassfish +* Apache Geronimo + +==== Configuration of CDI + +Implement a CDI extension that controls CDI based on configuration: +* Add beans +* Remove (veto) beans +* Add/remove interceptors +* Add/remove decorators +* Activate alternatives +* ... + +==== Configuration of Bean Validation + +* Add configurable validators. +* Configure bean validation based on configuration +* ... + +=== JNDI Support + +Write a +JCA+ adapter to provide configuration data through JNDI. + +==== Configure JSF + +Use the JSF +XML Document+ event to completely configure JSF. + +==== Configure Web Services + +Provide a WebServiceProviderFactory that may be configured. + +==== Configure JPA + +Provide an implementation that allows configuration of persistence units. Talk with JPA EG people to see if we can +get an SPI to hook in a stadardized way. + +==== Configure EJBs + +Provide an implementation that allows configuration of EJBs and MDBs: + +* Register beans +* Unregister/disable beans +* Intercept beans +* Support Configuration Injection (in the worst case using a standard Interceptor, provide supporting artifacts to + help developers to achive this easily). +* Talk with EE8 Umbrella EG (Bill Shanon, Linda DeMichels) on a feasible SPI for EE8, if possible join the EG. + +==== Configure ... + +Just think of any Java EE aspects that might be worth to be configured. If it can be done, e.g. by managing CDI managed +resources, it might be easy. For others it is a good idea to discuss things with our matter of experts... + +== Special Goodies + +=== Maintenance Mode Servlet Filter + +Provide a servlet filter that is capable of switching to maintenance mode, based on configuration. Similarly also a forwarding +servlet could be useful, wehere only request based on configuration are forwarded, other might be rejected or dropped +as configured. + +=== Dynamic Camel Routes + +Provides dynamic (configurable) Camel routes, e.g. usable within ServiceMix or standalone. + +=== Dynamic CXF + +Provides dynamic (configurable) CXF adapters, e.g. usable within ServiceMix or standalone. + +=== Configurable Apache MQ + +Provides an implementation for configuring Apache MQ. + +=== Dynamic ... + +Interested to see what other ideas are around. Let us know! + http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cd513bf3/docs/src/main/asciidoc/design/0_UseCases.adoc ---------------------------------------------------------------------- diff --git a/docs/src/main/asciidoc/design/0_UseCases.adoc b/docs/src/main/asciidoc/design/0_UseCases.adoc new file mode 100644 index 0000000..8ec833e --- /dev/null +++ b/docs/src/main/asciidoc/design/0_UseCases.adoc @@ -0,0 +1,319 @@ +// 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. + +<<< +[[UseCases]] +== Use Cases + +This section describes some, but not all, of the use cases that should be covered by Tamaya. + + +[[UCSimpleAccess]] +=== Simple Property Access (UC 1) + +Tamaya should provide a simple Java API for accessing configuration. Hereby + +* Configuration is organized as key/value pairs. This basically can be modeled as +Map<String,String>+ +* Configuration should be as simple as possible. A +Map<String,String>+ instance has methods that may not + be used in many use cases and/or are not easy to implement. Currently the following functionality + must be supported: + ** access a value by key (+get+) + ** check if a value is present (+containsKey+) + ** get a set of all defined keys (+keySet+) + ** a property provider must be convertable to a +Map+, by calling +toMap()+ + ** a property provider must get access to its meta information. +* The API must never return null. +* The API should support undefined values. +* The API must support passing default values, to be returned if a value is undefined. +* The API must allow to throw exceptions, when a value is undefined. + Customized exceptions hereby should be supported. +* Properties can be stored in the classpath, on a file. +* Properties can be stored in properties, xml-properties or ini-format. +* Properties can also be provided as properties, or as a Map<String,String> + + +[[UCAutoConfig]] +=== Automatic Configuration + +* Tamaya should provide a feature for automatic configuration, where properties can be annotated. +* Hereby the lifecycle of the instances configured should not be managed by Tamaya. +* String as well as other types should be supported. +* It should be possible to define default values to be used, if no valid value is present. +* It should be possible to define dynamic expressions, at least for default values. +* The values configured should be reinjected, if the underlying configuration changes. +* Reinjection should be controllable by an injection policy. +* It should be possible to evaluate multiple keys, e.g. current keys, and as a backup deprecated keys + from former application releases. +* The type conversion of the properties injected should be configurable. +* The value evaluated for a property (before type conversion) may be adaptable as well. +* It should be possible to observe configuration changes. + +The most simplest way is using injection, e.g. a POJO can be written as follows: + +[source, java] +.Configured POJO Example +---------------------------------------------------- +public class MyPojo { + @ConfigProperty("myCurrency") + @DefaultValue("CHF") + private String currency; + + @ConfigProperty("myCurrencyRate") + private Long currencyRate; +} +---------------------------------------------------- + +The instance then can be passed for being configured: + +[source, java] +.Configuring a POJO +---------------------------------------------------- +MyPojo instance = new MyPojo(); +Configuration.configure(instance); +---------------------------------------------------- + +[[UCTemplates]] +=== Configuration Templates + +For type safe configuration clients should be able to define an interface that is implemented by the +configuration system: + +* clients define an interface and annotate it as required +* the interface methods must not take any arguments +* the configuration system can be called to return such an interface implementation. +* the configuration system returns a proxy hereby providing the values required. +* similar to configured types also templates support multiple values and custom adapters. +* It is possible to listen on configuration changes for templates, so users of the templates + may react on configuration changes. + +A template hereby is modelled by annotating an interface with the same annotations as for +configured classes: + +[source, java] +.Type Safe Configuration Template Example +---------------------------------------------------- +public interface MyConfig { + @ConfiguredProperty("myCurrency") + @DefaultValue("CHF") + String getCurrency(); + + @ConfiguredProperty("myCurrencyRate") + Long getCurrencyRate(); + +} +---------------------------------------------------- + +The configuration system will then provide the interface as follows: + +[source, java] +.Accessing a type safe Configuration Template +---------------------------------------------------- +MyConfig config = Configuration.of(MyConfig.class); +---------------------------------------------------- + +Finally a +Configuration+ itself can be accessed as template as well, which +provides full access to all features: + +[source, java] +.Accessing a Configuration +---------------------------------------------------- +Configuration config = Configuration.of(Configuration.class); +---------------------------------------------------- + + +[[UCSimpleConfiguration]] +=== Simple Property Based Configuration + +In this most simple usage scenario an application is configured by some property files contained in the +Java archive. + +* It provides default property files in the formats defined by the JDK within its application archive. +* It allows to override settings by system properties. +* It is able to consider command line arguments as well. + + +[[UCAdvancedPropertyBasedConfiguration]] +=== Advanced Property Based Configuration + +Enhancing the previous scenario, we might as well consider the current environment. Saying that our overriding mechanisms +must be improved, since + +* some environment settings should not be overridable +* some defaults should be overridden by environment or system properties, whereas others may not +* Additionally the user may have an option, where he is allowed to define an external configuration file that should be used to configure + the application. This is especially useful for applications with lots of command line options (under windows even command + execution may fail die to exceeding command length). +* Finally application developers may have their own formats in place, so the system should be able to support these formats. + + +[[UCModularizedConfiguration]] +=== Modularized Configuration + +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 want to have their own "module configuration". +* Some modules require a certain subset of keys to be read at once into a Map. +* Products contain multiple modules, which per product are configured separately. + + +[[UCTypeSupport]] +=== Extended Type Support + +Application configuration must also support non String types such as primitives, wrapper types, math types +and date/time values. Basically each type that can be created from a String in more standardized way should +supported. This should be even possible for types not known at build time of possible. Type conversion hereby +should be flexible. + +[[UCDynamicProvisioning]] +=== Dynamic Provisioning + +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. + +Consequently: + +-> Ensure Configuration can be transferred over the network easily. + +-> Whereas many people will no think serializability is the solution, it would be much more useful to define + a text based format for serialization, e.g. in +XML+ or +JSON+. + +-> Similarly a management API should be defined, which allows to inspect the configuration in place, e.g. using + JMX or REST services. + +[[UCJavaEE]] +=== Java EE + +Considering Java EE different aspects should be considered: + +* Java EE is a complex multi-layered architecture with different levels of runtime contexts: +** application server boot level (system classloader), +** (optional) deployment/undeployment of ears (ear classloader), +** (optional) deployment/undeployment of web applications (war classloader), +** different runtime setups, e.g. EJB calls, MDB execution, Servlet Requests, scheduled and timed executions. +* Configuring administrative resources (e.g. datasources, users, security etc) is typically vendor specific. +* The environment is inherently multi-threaded. + +Given that a couple of additional requirements araise: + +-> Configuration must be contextual, depending on the current runtime context (e.g. boot level, ear, war, ...). + +-> Hereby contextual aspects can even exceed the levels described above, e.g. for SaaS scenarios. + +-> Resources can be unloaded, e.g. wars, ears can be restarted. + +-> The different contextual levels can also be used for overriding, e.g. application specific configuration +may override ear or system configuration. + +-> Configuration may be read from different sources (different classloaders, files, databases, remote locations). + +-> Configuration may be read in different formats (deployment descriptors, +ServiceLoader+ configuration, alt-DD feature, ...) + +-> JSF also knows the concept of stages. + +-> Many SPI's of Java EE require the implementation of some well defined Java interface, so it would be useful if the + configuration solution supports easy implementation of such instances. + +-> In general it would be useful to model the +Environment+ explicitly. + +-> Configuration used as preferences is writable as well. This requires mutability to be modelled in way, without the + need of synchronization. + +-> JNDI can be used for configuration as well. + +[[UCMultiTenancy]] +=== Scenario MultiTenancy +In multi tenancy setups a hierarchical/graph model of contexts for configurations is required. For example there might +be some kind of layering as follows: + +* Layer 0: Low level system configuration +* Layer 1: Domain configuration +* Layer 2: Default App configuration +* Layer 3: Tenant specific configuration +* Layer 4: User specific configuration + +Configurations made in the tenant or user layer override the default app configuration etc. + +-> It must be possible to structure Configuration in layers that can override/extend each other. + +-> The current environment must be capable of mapping tenant, user and other aspects, so a corresponding configuration + (or layer) can be derived. + +[[UCJavaAPI]] +=== Accessing Configuration + +So far we described much how configuration must be organized and managed, but we got not concrete, how it is accessed. +Basically there are two basic scenarios to be distinguished, which mainly depend on the way how the lifecycle of a component +to be configured is managed: + +* If the lifecycle is managed manually by the developer, the configuration system +** can inject configuration values, when explicitly called to to so +** can provide an accessor for configuration. +* If the lifecycle is managed by some container such as a DI container, the configuration + system should leverage the functionality of the container, where possible. + + + + +[[UCTesting]] +=== Testing +When testing a Java solution, it must be possible to easily control the configuration provided, so isolated +component tests can be written effectively. Also it should be possible to control/isolate the configuration level for +each test case. + +-> isolation of configuration services is required + +-> API for controlling the configuration provided, required for according implementations in the testing frameworks. + +[[UCStaging]] +=== Staging +Different companies go through different staging levels during the development of software components. Currently only +rarely the EE frameworks support staging aspects, nevertheless no broader, well modelled staging concept is defined. +Different companies also have different staging or sub-staging levels in place, which also must be reflected. +Especially with sub-stages inheritance of stage related configuration is common sense and should be supported. + +-> Main stages available and to be supported must be defined. + +-> Enable additional stages to be added, so also custom stages can be supported. + + +[[UCCotsIntegration]] +=== Custom of the Shelf (COTS) Integration +When buying software from an external software company it is often very cumbersome to integrate, adapt and customize +third party software to the internal operational requirements. Especially, when software is delivered as ear modules +portability is often very difficult and time consuming. Configuration should enable COTS providers to define a +customization contract, which also can be part of the COTS software interface and integration specifications. This +would allow operations to better control and configure third party solutions as possible, whereas in the evaluation +phase the integration and configuration options can explicitly be defined. + +-> It must be possible to document configuration aspects supported. + +-> Configuration must be overridable from external sources (the operations which must operate the COTS solution). + http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cd513bf3/docs/src/main/asciidoc/design/1_Requirements.adoc ---------------------------------------------------------------------- diff --git a/docs/src/main/asciidoc/design/1_Requirements.adoc b/docs/src/main/asciidoc/design/1_Requirements.adoc new file mode 100644 index 0000000..6367ed3 --- /dev/null +++ b/docs/src/main/asciidoc/design/1_Requirements.adoc @@ -0,0 +1,50 @@ +// 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. + +<<< +[[Requirements]] +== Requirements +=== Core Configuration Requirements +Based on the scope and use cases described above the following core requirements can be identified: + +* Configuration is modelled as String based key/value pairs. +* Configuration can be combined using the GoF composite pattern. Hereby different composition policies can be applied, such as + ** override: subsequent entries override existing ones. + ** substraction: keys present in the second configuration will be removed. + ** union-resolve: key/values were added, in case of conflicts a +ConfigException+ must be thrown. + ** union-ignore: similar to union, whereas duplicates are ignored (leaving the initial value loaded). + ** intersection: + +[[RequirementsServer]] +=== Server Configuration Requirements +shskjdhskhds sdkj ksjdks skjdskjd: + +. Req1 +. Req2 + +[[RequirementsExtensions]] +=== Extensions Requirements +shskjdhskhds sdkj ksjdks skjdskjd: + +. Req1 +. Req2 + +[[RequirementsNonFunctional]] +=== Non Functional Requirements +. Req1 +. Req2 +