| = Apache Tamaya -- Extension: Injection |
| |
| :name: Tamaya |
| :rootpackage: org.apache.tamaya.ext.injection |
| :title: Apache Tamaya Extension: Injection |
| :revnumber: 0.1.1 |
| :revremark: Incubator |
| :revdate: March 2015 |
| :longversion: {revnumber} ({revremark}) {revdate} |
| :authorinitials: ATR |
| :author: Anatole Tresch |
| :email: <anatole@apache.org> |
| :source-highlighter: coderay |
| :website: http://tamaya.incubator.apache.org/ |
| :toc: |
| :toc-placement: manual |
| :encoding: UTF-8 |
| :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. |
| ''' |
| |
| <<< |
| |
| toc::[] |
| |
| <<< |
| :numbered!: |
| <<< |
| [[Core]] |
| == Tamaya Injection (Extension Module) |
| === Overview |
| |
| Tamaya Injection is an extension module. Refer to the link:modules.html[extensions documentation] for further details |
| about modules. |
| |
| Tamaya Injection provides functionality for injecting configured values into beans, or creating configuration |
| template instances. |
| |
| Inversion of Control (aka IoC/the Hollywood Principle) has proven to be very useful and effective in avoiding boilerplate |
| code. In Java there are different frameworks available that all provide IoC mechanisms. Unfortunately IoC is not a |
| built-in language feature. So for a portable solution that works also in Java SE Tamaya itself has to provide the |
| according injection services. This module adds this functionality to Tamaya. |
| |
| === Compatibility |
| |
| The module is based on Java 7, so it can be used with Java 7 and beyond. |
| |
| === Installation |
| |
| To benefit from configuration event support you only must add the corresponding dependency to your module: |
| |
| [source, xml] |
| ----------------------------------------------- |
| <dependency> |
| <groupId>org.apache.tamaya.ext</groupId> |
| <artifactId>tamaya-injection</artifactId> |
| <version>{tamayaVersion}</version> |
| </dependency> |
| ----------------------------------------------- |
| |
| === Core Concepts |
| |
| As an example refer to the following |
| code snippet: |
| |
| [source,java] |
| .Annotated Example Class |
| -------------------------------------------- |
| package foo.bar; |
| |
| public class ConfiguredClass{ |
| |
| // resolved by default, using property name, class and package name: foo.bar.ConfiguredClass.testProperty |
| private String testProperty; |
| |
| @ConfiguredProperty(keys={"a.b.c.key1","a.b.legacyKey",area1.key2"}) |
| @DefaultValue("The current \\${JAVA_HOME} env property is ${env:JAVA_HOME}.") |
| String value1; |
| |
| // Using a (default) String -> Integer converter |
| @ConfiguredProperty(keys="a.b.c.key2") |
| private int value2; |
| |
| // resolved by default as foo.bar.ConfiguredClass.accessUrl |
| // Using a (default) String -> URL converter |
| @DefaultValue("http://127.0.0.1:8080/res/api/v1/info.json") |
| private URL accessUrl; |
| |
| // Config injection disabled for this property |
| @NoConfig |
| private Integer int1; |
| |
| // Overriding the String -> BigDecimal converter with a custom implementation. |
| @ConfiguredProperty(keys="BD") |
| @WithPropertyConverter(MyBigDecimalRoundingAdapter.class) |
| private BigDecimal bigNumber; |
| |
| ... |
| } |
| -------------------------------------------- |
| |
| The class does not show all (but most) possibilities provided. Configuring an instance of the |
| class using Tamaya is very simple. The only thing is to pass the instance to Tamaya to let |
| Tamaya inject the configuration (or throw a +ConfigException+, if this is not possible): |
| |
| [source,java] |
| .Configuring the +ConfiguredClass+ Instance |
| -------------------------------------------- |
| ConfiguredClass classInstance = new ConfiguredClass(); |
| ConfigurationInjector.configure(configuredClass); |
| -------------------------------------------- |
| |
| === The Annotations in detail |
| ==== The ConfigurationInjector |
| |
| The +ConfigurationInjector+ interface provides methods that allow any kind of instances to be configured |
| by passing the instances to +T ConfigurationInjector.getInstance().configure(T);+. The classes passed |
| hereby must not be annotated with +@ConfiguredProperty+ for being configurable. By default Tamaya |
| tries to determine configuration for each property of an instance passed, using the following resolution policy: |
| |
| Given a class +a.b.MyClass+ and a field +myField+ it would try to look up the following keys: |
| [source, listing] |
| -------------------------------------------- |
| a.b.MyClass.myField |
| a.b.MyClass.my-field |
| MyClass.myField |
| MyClass.my-field |
| myField |
| my-field |
| -------------------------------------------- |
| |
| So given the following properties: |
| |
| [source, properties] |
| -------------------------------------------- |
| a.b.Tenant.id=1234 |
| Tenant.description=Any kind of tenant. |
| name=<unnamed> |
| -------------------------------------------- |
| |
| The following bean can be configured as follows: |
| |
| [source, java] |
| -------------------------------------------- |
| package a.b; |
| |
| @ConfiguredType(autoConfigure=true) |
| public final class Tenant{ |
| private int id; |
| private String name; |
| private String description; |
| |
| public int getId(){ |
| return id; |
| } |
| public String getName(){ |
| return name; |
| } |
| public String getDescription(){ |
| return description; |
| } |
| } |
| |
| Tenant tenant = ConfigurationInjector.getInstance().configure(new Tenant()); |
| -------------------------------------------- |
| |
| ==== Accessing ConfiguredItemSupplier instances |
| |
| In many cases you want to create a supplier that simply creates instances that are correctly configured as defined |
| by the current context. This can be done using +Suppliers+: |
| |
| [source, java] |
| -------------------------------------------- |
| ConfiguredItemSupplier<Tenant> configuredTenantSupplier = ConfigurationInjector.getInstance().getConfiguredSupplier( |
| new ConfiguredItemSupplier<Tenant>(){ |
| public Tenant get(){ |
| return new Tenant(); |
| } |
| }); |
| -------------------------------------------- |
| |
| With Java 8 it's even more simpler: |
| |
| [source, java] |
| -------------------------------------------- |
| ConfiguredItemSupplier<Tenant> configuredTenantSupplier = ConfigurationInjector.getInstance().getConfiguredSupplier( |
| Tenant::new); |
| -------------------------------------------- |
| |
| Hereby this annotation can be used in multiple ways and combined with other annotations such as +@DefaultValue+, |
| +@WithLoadPolicy+, +@WithConfigOperator+, +@WithPropertyConverter+. |
| |
| ==== Minimal Example |
| |
| 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). |
| |
| ==== Using @DefaultValue |
| |
| In the next example we explicitly define the property value: |
| [source,java] |
| -------------------------------------------- |
| pubic class ConfiguredItem{ |
| |
| @ConfiguredProperty(keys={"aValue", "a.b.value","a.b.deprecated.value"}) |
| @DefaultValue("${env:java.version}") |
| private String aValue; |
| } |
| -------------------------------------------- |
| |
| ==== Inject a DynamicValue Property |
| |
| Within this example we evaluate a dynamic value. This mechanism allows you to listen for configuration changes and to |
| commit new values exactly, when convenient for you. |
| |
| [source,java] |
| -------------------------------------------- |
| pubic class ConfiguredItem{ |
| |
| @ConfiguredProperty(keys={"aValue", "a.b.value","a.b.deprecated.value"}) |
| @DefaultValue("${env:java.version}") |
| private DynamicValue aValue; |
| } |
| -------------------------------------------- |
| |
| The +DynamicValue+ provides you the following functionality: |
| |
| [source,java] |
| -------------------------------------------- |
| public interface DynamicValue<T> { |
| |
| enum UpdatePolicy{ |
| IMMEDIATE, |
| EXPLCIT, |
| NEVER, |
| LOG_AND_DISCARD |
| } |
| |
| T get(); |
| T getNewValue(); |
| T evaluateValue(); |
| T commitAndGet(); |
| void commit(); |
| void discard(); |
| boolean updateValue(); |
| |
| void setUpdatePolicy(UpdatePolicy updatePolicy); |
| UpdatePolicy getUpdatePolicy(); |
| void addListener(PropertyChangeListener l); |
| void removeListener(PropertyChangeListener l); |
| |
| boolean isPresent(); |
| T orElse(T other); |
| T orElseGet(ConfiguredItemSupplier<? extends T> other); |
| <X extends Throwable> T orElseThrow(ConfiguredItemSupplier<? extends X> exceptionSupplier) throws X; |
| |
| } |
| -------------------------------------------- |
| |
| Summarizing this class looks somehow similar to the new +Optional+ class added with Java 8. It provides |
| a wrapper class around a configured instance. Additionally this class provides functionality that gives |
| active control, to manage a configured value based on a ++LoadingPolicy+: |
| |
| * +IMMEDEATE+ means that when the configuration system detects a change on the underlying value, the new value |
| is automatically applied without any further notice. |
| * +EXPLICIT+ means that a new configuration value is signalled by setting the +newValue+ property. if +getNewValue()+ |
| returns a non null value, the new value can be applied by calling +commit()+. You can always access the newest value, |
| hereby implicitly applying it, by accessing it via +commitAndGet()+. Also it is possible ti ignore a change by calling |
| +discard()+. |
| * +NEVER+ means the configured value is evaluated once and never updated. All changes are silently discarded. |
| * +LOG_AND_DISCARD+ similar to +NEVER+, but changes are logged before they are discarded. |
| |
| Summarizing a +DynamicValue+ allows you |
| |
| * to reload actively updates of configured values. |
| * update implicitly or explicitly all changes on the value. |
| * add listeners that observe changes of a certain value. |
| |
| Dynamic values also allow on-the-fly reevaluation of the value by calling +evaluateValue()+. Hereby the value of the |
| instance is not changed. |
| |
| |
| ==== Ommitting Injection using @NoConfig |
| |
| Adding the @NoConfig annotation prevents a field or method to be selected (mostly auto-selected) for |
| configuration. This is especially useful, if a type is annotated as @ConfiguredType with auto-confiuration |
| turned on as follows: |
| |
| [source,java] |
| -------------------------------------------- |
| @ConfiguredType(autoConfigure=true) |
| pubic class ConfiguredItem{ |
| |
| @NoConfig |
| private transient int sum; |
| |
| private String a; |
| private String b; |
| Private String c; |
| } |
| -------------------------------------------- |
| |
| In this case the fields +a,b,c+ are configured, whereas the field +sum+ is ignored regarding |
| configuration. |
| |
| ==== Adding custom operators using @WithConfigOperator |
| |
| The @WithConfigOperator annotation allows you define a class of type +ConfigOperator+, to being applied |
| to the final +Configuration+, BEFORE the value is injected. This can be used for various use cases, e.g. |
| filtering or validating the visible properties for a certain use case. |
| |
| [source,java] |
| -------------------------------------------- |
| |
| @WithConfigOperator(MyConfigView.class) |
| pubic class ConfiguredItem{ |
| |
| @ConfiguredProperty |
| private String a; |
| |
| } |
| -------------------------------------------- |
| |
| |
| ==== Adding custom property converters using @WithPropertyConverter |
| |
| The @WithPropertyConverter annotation allows you to define a class of type +PropertyConverter+, to be applied |
| on a property configured to convert the String value to the expected injected type. This can be used for |
| various use cases, e.g. adding custom formats, validation, decryption. |
| |
| [source,java] |
| -------------------------------------------- |
| |
| pubic class ConfiguredItem{ |
| |
| @WithPropertyConverter(MyPropertyConverter.class) |
| @ConfiguredProperty |
| private String a; |
| |
| } |
| -------------------------------------------- |
| |
| |
| ==== Defining the loading policy to be applied to configured values using @WithLoadPolicy |
| |
| The @WithLoadPolicy annotation allows to define the loading behaviour to be applied. The +LoadPolicy+ |
| enum hereby defines the various loading modes. |
| |
| [source,java] |
| -------------------------------------------- |
| |
| @WithLoadPolicy(LoadPolicy.NEVER) |
| pubic class BootTimeStableConfig{ |
| |
| @WithPropertyConverter(MyPropertyConverter.class) |
| @ConfiguredProperty |
| private String a; |
| |
| } |
| -------------------------------------------- |
| |