| :jbake-type: page |
| :jbake-status: published |
| |
| |
| = Why should you rely on Tamaya? |
| |
| There are several reasons, why you should choose Tamaya, some of them: |
| |
| * *Tamaya is useful*: Tamaya ships with a type safe, easy to use API and SPI, which can help you to decouple your |
| code from configuration concerns. |
| * *Tamaya is flexible*: Regardless of working |
| with Vanilla Java SE or with OSGi or Jakarta EE, Tamaya can be the tool |
| of choice for your configuration concerns. |
| * *Tamaya is extendible*: Tamaya comes with a lean core API. Additional features can |
| be added easily by simply adding them to the project classpath as needed. |
| Tamaya does not fill up your memory with things you wont ever need. |
| * *Tamaya is easy*: Tamaya comes with a flexible auto-discovery mechanism to lookup |
| the property sources and define their individual significance. |
| * *Tamaya is runtime-independent*: Tamaya runs on every JVM platform, since it's core is written in pure |
| Java. |
| * *Tamaya is well documented*: Both the API as well as the extension are all well documented. |
| * *Tamaya is Apache*: Tamaya is community at its best, no vendor lock-in, no payed members, just |
| the Apache Way! |
| |
| A good way to continue is probably looking at the use cases we had in mind, when implementing Tamaya... |
| |
| = Use Cases and Requirements |
| |
| toc::[] |
| |
| == Use Cases |
| |
| === 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. |
| |
| [source,java] |
| ------------------------------------------------------------ |
| 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: |
| |
| [source,java] |
| ------------------------------------------------------------ |
| @Inject |
| private Configuration cfg; |
| ------------------------------------------------------------ |
| |
| |
| === 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+. |
| |
| |
| === 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. |
| |
| |
| === 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.ui.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. |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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 identify the current layer, so configuration used may be adapted. |
| |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| === 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 |
| |
| |
| === 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. |
| |
| |
| === 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. |
| |
| |
| [[Requirements]] |
| == Requirements |
| === Core Configuration Requirements |
| ==== General |
| |
| Tamaya must provide a Java SE API for accessing key/value based configuration. Hereby |
| |
| * +Configuration+ is modelled by an interface |
| * +Configuration+ is organized as key/value pairs, using a subset of functionality present on +Map<String,String>+ as |
| follows: |
| ** access a value by key (+get+) |
| ** check if a value is present (+containsKey+) |
| ** get a set of all defined keys (+keySet+) |
| ** a configuration must be convertible to a +Map+, by calling +toMap()+ |
| ** a configuration must provide access to its meta information. |
| * +Configuration+ value access methods must never return null. |
| * The API must 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 or accessible by URL. |
| * Properties can be stored minimally in properties, xml-properties or ini-format. |
| |
| |
| ==== Minimalistic Property Source |
| |
| For enabling easy integration of custom built configuration sources a minimalistic API/SPI must be defined, that |
| |
| * is modelled by an interface |
| * is a minimal subset of +Configuration+ necessary to implement a configuration. |
| * must be convertible to a "Configuration+. |
| |
| ==== Extension Points |
| |
| For supporting more complex scenarios, +Configuration+ |
| |
| * must implement the composite pattern, meaning new +Configuration+ instances can be created by combining existing |
| configurations. |
| * must be adaptable, by creating a new configuration by applying a +UnaryOperator<COnfiguration>+ to it. |
| * must be queryable, by passing a +ConfigQuery+ to an +Configuration+ instance. |
| |
| |
| ==== Type Safety |
| |
| Besides Strings +Configuration+ should also support the following types: |
| |
| * Primitive types |
| * Wrapper types |
| * All other types (by using a +PropertyAdapter+ |
| |
| Hereby type conversion should be done as follows: |
| |
| . Check if for the given target type an explicit adapter is registered, if so, use the registered adapter. |
| . If no adapter is present, check if the target type T has static methods called +T of(String), T getInstance(String), T valueOf(String), T from(String)+. If so |
| use this method to create the non value of T. |
| . Check if the target type has a constructor T(String). If so, try to instantiate an instance using the constructor. |
| . Give up, throw a IllegalArgument exception. |
| |
| === Configuration Fomats |
| |
| By default Tamaya support the following configuration formats: |
| |
| * .properties |
| * .xml properties |
| * .ini files |
| |
| It must be possible to add additional formats by registering them with the current +ServiceContext+. |
| |
| === Mutability |
| |
| * Configurations can be mutable, mutability can be accessed as a property. |
| * Configuration can be changed by collecting the changes into a +ConfigCHangeSet+ and apply this set to the |
| given +Configuration+ instance. |
| * Besides the points above, +Configuration+ is immutable. |
| |
| === Serializability and Immutability of Configuration |
| |
| * Configuration is modelled as a service. Therefore serialization may not work. This can be mitigated by adding |
| a freeze feature, where the know key/value pairs are extracted into an immutable and serializable form. |
| |
| === Configuration Combination Requirements |
| |
| At least the following composition policies must be supported: |
| |
| * override: subsequent entries override existing ones. |
| * aggregate-exception: key/values were added, in case of conflicts a +ConfigException+ must be thrown. |
| * aggregate-ignore-duplicates: similar to union, whereas duplicates are ignored (leaving the initial value loaded). |
| * aggregate-combine: conflicting entries were resolved by adding them both to the target configuration by |
| redefining partial keys. |
| * custom: any function determining the key/values to be kept must be possible |
| |
| When combining configuration it must also be possible to override (file/classpath) configuration by |
| |
| * system properties. |
| * command line arguments. |
| |
| |
| === Configuration Injection |
| |
| As metnioned configuration can be injected by passing a unconfigured instance of an annotated class to the |
| +Configuration.configure+ static method: |
| |
| [source, java] |
| .Configuring a POJO |
| ---------------------------------------------------- |
| MyPojo instance = new MyPojo(); |
| Configuration.configure(instance); |
| ---------------------------------------------------- |
| |
| Hereby |
| * It must be possible to define default values to be used, if no valid value is present. |
| * It must be possible to define dynamic expressions, at least for default values. |
| * The values configured can be reinjected, if the underlying configuration changes. This should also be the case |
| for final classes, such as Strings. |
| * Reinjection should be controllable by an loading policy. |
| * It must be possible to evaluate multiple keys, e.g. current keys, and as a backup deprecated keys |
| from former application releases. |
| * It must be possible to evaluate multiple configurations. |
| * The type conversion of the properties injected must be configurable, by defining a +PropertyAdapter+. |
| * The value evaluated for a property (before type conversion) must be adaptable as well. |
| * It must be possible to observe configuration changes. |
| |
| The following annotations must be present at least: |
| |
| * *@ConfiguredProperty* defining the key of the property to be evaluated. It takes an optional value, defining the |
| property name. It must be possible to add multiple annotations of this kind to define an order of evaluation |
| of possible keys. |
| * *@DefaultValue* (optional) defines a default String value, to be used, when no other key is present. |
| * *@WithConfig* (optional) defines the name of the configuration to be used. Similar to +@ConfiguredProperty+ multiple |
| configuration can be defined for lookup. |
| * *@WithConfigOperator* allows to adapt the String value evaluated, *before* it is passed as input to injection or |
| type conversion. |
| * *@WithPropertyAdapter* allows to adapt the conversion to the required target type, hereby overriding any default |
| conversion in place. |
| * *@WithLoadPolicy* allows to define the policy for (re)injection of configured values. |
| * *@ObservesConfigChange* allows to annotate methods that should be called on configuration changes. |
| * *@DefaultAreas" allows to define a key prefix key to be used for the configured key, if no absolute key |
| is defined. |
| |
| === Configuration Templates |
| |
| For type safe configuration clients should be able to define an interface and let it implement by the |
| configuration system based on +Configuration+ available: |
| |
| * Clients define an interface and annotate it as required (similar to above) |
| * 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 type-safe access 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. |
| |
| The following snippet illustrates the requirements: |
| |
| [source, java] |
| .Type Safe Configuration Template Example |
| ---------------------------------------------------- |
| public interface MyConfig { |
| |
| @ConfiguredProperty("myCurrency") |
| @DefaultValue("CHF") |
| String getCurrency(); |
| |
| @ConfiguredProperty("myCurrencyRate") |
| Long getCurrencyRate(); |
| |
| @ConfigChange |
| default configChanged(ConfigChange event){ |
| ... |
| } |
| |
| } |
| ---------------------------------------------------- |
| |
| Templates can be accessed by calling the +Configuration.current(Class)+ method: |
| |
| [source, java] |
| .Accessing a type safe Configuration Template |
| ---------------------------------------------------- |
| MyConfig config = Configuration.current(MyConfig.class); |
| ---------------------------------------------------- |
| |
| [[RequirementsServer]] |
| === Server Configuration Requirements |
| |
| * Ensure Configuration can be transferred over the network easily. |
| * Beside serializability text based formats for serialization in +XML+ and +JSON+ must be defined. |
| * A management API must be defined, which allows to inspect the configuration in place, e.g. using |
| JMX or REST services. |
| |
| [[RequirementsJavaEE]] |
| |
| Java EE leads to the following requirements: |
| |
| * 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. |
| |
| [[RequirementsMultitenancy]] |
| |
| Configurations made in the tenant or user layer override the default app configuration etc., so |
| |
| * 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. |
| |
| [[RequirementsExtensions]] |
| === Extensions Requirements |
| |
| It must be possible to easily add additional functionality by implementing external functional interfaces operating |
| on +Configuration+. |
| |
| * +UnaryOperator<Configuration>+ for converting into other version of +Configuration+. |
| * +ConfigQuery<T>+ extending +Function<T, Configuration>+. |
| |
| [[RequirementsNonFunctional]] |
| === Non Functional Requirements |
| The following non-functional requirements must be met: |
| |
| * tbd |