Yaml values¶
Guicey introspects Configuration
object instance using jackson serialization api to allow direct access to yaml configuration
values.
Introspected configuration is accessible as:
- Direct guice bindigs for unique sub objects, and all properties
- Bindings for manually qualified properties
ConfigurationTree
object (binding) containing all introspection data (could be used for manual searches and, for example, for reports)- Guice modules and guicey bundles could access introspected configuration with help of
ConfigurationAwareModule
interface andGuiceyEnvironment
object.
Warning
Jackson will see all properties that either have getter and setter or annotated with @JsonProperty
. For example,
public class MyConfig extends Configuration {
private String one // visible (getter and setter)
@JsonProperty
private String two // visible (annotation)
private String three // invisible (no getter)
public void setOne(String one) { ... }
public String getOne() { ... }
public void setTwo(String two) { ... }
public void setThree(String three) { ... }
}
Tip
To prevent binding of configuration property use @JsonIgnore
on property getter
private String prop
// dropwizard will set value from yaml
public void setProp(Stirng prop) { ... }
// property will not be available as path binding
@JsonIgnore
public String getProp() { ... }
Important
Guice does not allow null value bindings by default, so if you bind configuration property with null value injector creation would fail.
To workaround it, use @jakarta.inject.Nullable
for injected field.
Unique sub configuration¶
It is quite common to group configuration properties into sub objects like:
public class MyConfig extends Configuration {
@JsonProperty AuthConfig auth;
@JsonProperty DbConfig db;
}
Guicey detects such unique objects and provides direct bindings for them:
@Inject @Config AuthConfig auth;
@Inject @Config DbConfig db;
This is very useful for re-usable modules, which are not aware of your configuration object structure, but require only one sub configuration object:
public class MyConfig extends Configuration {
@JsonProperty FeatureXConfig featureX;
}
Somewhere in module service:
public class FeatureXService {
@Inject @Config FeatureXConfig featureX;
}
Important
Sub configuration object uniqueness is checked as direct match, so you may have
@JsonProperty SubConfig sub
@JsonProperty SubConfigExt subExt
class SubConfigExt extends SubConfig
, but still both objects would be considered unique.
Whereas
@JsonProperty SubConfig sub1
@JsonProperty SubConfig sub2
Tip
Guicey bundles and guice modules also could use sub configuration objects directly:
GuiceyEnvironment#configuration(SubConfig.class)
DropwizardAwareModule#configuration(SubConfig.class)
Qualified bindings¶
Automatic unique objects bindings require using guicey binding annotation. In some cases, this is not possible: for example, generic 3rd party guice module. You can use qualified bindings then.
The idea is simple: just annotate any configuration property or getter with a qualified
annotation and it would be bound in guice context. Moreover, if multiple properties
(same type!) would be annotated with the same annotation - they would be bound as Set
.
For example,
public class MyConfig extends Configuration {
@Named("custom")
private String prop1;
@CustomQualifier
private SubObj obj1 = new SubObj();
Tip
Custom qualifying annotation must be annotated with guice @BindingAnnotation
or jakarta @Qualifier
(see guice and jakarta @Named
annotations as an example).
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@BindingAnnotation
public @interface CustomQualifier {}
Would mean two additional bindings:
@Inject @Named("custom") String prop1;
@Inject @CustomQualifier SubObj obj1;
Important
If you expect null values then identify it (otherwise injector creation would fail for null value):
@Inject @Named("custom") @jakarta.inject.Nullable String prop1;
And binding like this:
@Named("sub-prop")
private String prop2;
@Named("sub-prop")
private String prop3;
Would result in aggregated binding:
@Inject @Named("sub-prop") Set<String> prop23;
Note
Qualifying annotations may be used on any configuration depth (not only in the root configuration object).
Core dropwizard objects could also be bound with a qualified overridden getter:
@Named("metrics")
@Override
MetricsFactory getMetricsFactory() {
return super.getMetricsFactory();
}
Note
All custom bindings are visible in the configuration report: .printCustomConfigurationBindings()
Tip
Guicey bundles and guice modules also could use qualified configuration values directly:
GuiceyEnvironment#annotatedConfiguration(Ann.class)
DropwizardAwareModule#annotatedConfiguration(Ann.class)
Configuration by path¶
All visible configuration paths values are directly bindable:
public class MyConfig extends Configuration {
SubConf sub;
}
public class SubConf {
String smth;
List<String> values;
}
@Inject @Config("sub") SubConf sub;
@Inject @Config("sub.smth") String smth;
@Inject @Config("sub.values") List<String> values;
Note
Path bindings are available even for null values. For example, if sub configuration object is null, all it's sub paths will still be available (by class declarations). The only exception is conditional mapping like dropwizard "server" when available paths could change, depending on configuration (what configuration class will be used)
Note
Generified types are bound only with generics (with all available type information).
If you will have SubConf<T> sub
in config, then it will be bound with correct generic SubConfig<String>
(suppose generic T is declared as String).
Value type, declared in configuration class is used for binding, but there are two exceptions.
If declared type is declared as collection (Set, List, Map) implementation then binding will use base collection interface:
ArrayList<String> value
@Inject @Config("value") List<String> vlaue;
If, for some (unforgivable) reason, property is declared as Object in configuration, then binding type will depend on value presence:
@Config("path") Object val
- when value is null@Config("path") ValueType val
- actual value type, when value is not null
It is assumed that in such case value would be always present (some sort of property-selected binding, like dropwizard "server").
Tip
You can declare you own additional bindings using ConfigurationTree
(accessible from guice module),
which contains all paths information (including declaration and actual types with generics).
Introspected configuration¶
ConfigurationTree
object provides access for introspected configuration tree:
getRootTypes()
- all classes in configuration hierarchy (including interfaces)getPaths()
- all paths (including all steps ("sub", "sub.value"))getUniqueTypePaths()
- paths of unique sub configuration types
Each path item (ConfigPath
) contains:
- Root path reference ("sub.value" reference "sub")
- Child sub-paths ("sub" reference "sub.value")
- Declaration class (type used in configuration class)
- Value type (type of actual value; when value null - declaration type (but they still could be different for collections))
- Current path name
- Current path value
- Generics for declaration and value types (may be incomplete for value type)
- Custom type marker: contains sub paths or just looks like sub configuration
- Declaration type (class where property was declared - configuration object containing property)
You can traverse up or down from any path (tree structure).
ConfigurationTree
provides basic search methods (more as usage example):
findByPath(String)
- search path by case-insensitive matchfindAllByType(Class)
- find all paths with assignable declared valuefindAllFrom(Class<? extends Configuration>)
- find all paths, started in specified configuration classfindAllRootPaths()
- get all root paths (1st level paths)findAllRootPathsFrom(Class<? extends Configuration>)
- all 1st level paths of configuration classvalueByPath(String)
- return path value or null if value null or path not existsvaluesByType(Class)
- all not null values with assignable typevalueByType(Class)
- first not null value with assignable typevalueByUniqueDeclaredType(Class)
- value of unique sub configuration or null if value is null or config is not unique
Methods to work with qualified configurations:
findAllByAnnotation(ann | Class)
- find all annotated properties by annotation instance or annotation classfindByAnnotation(ann | Class)
- find exactly one annotated property or throw error if more then one foundannotatatedValues(ann | Class)
- all non-null values from annotated propertiesannotatatedValue(ann | Class)
- an annotated property value (throw error if more properties annotated)
Tip
Searching by annotation instance if required for annotations with "state". For example,
@Named("something")
and @Named("other")
are different qualifying annotations,
and searching only by type would be incorrect in this case.
It is not possible to directly create annotation instance, but any annotation
is and interface and could be implemented. It is important that real annotation
(from class) and provided object would be equal. As an example see how guice Names.named()
consrtructs "@Named" insatnces.
Paths are sorted by configuration class (to put custom properties upper) and by path name (for predictable paths order).
Disable configuration introspection¶
Introspection process should not fail application startup. In worse case it will show warning log that property can't be introspected:
WARN [2018-07-23 09:11:13,034] ru.vyarus.dropwizard.guice.module.yaml.ConfigTreeBuilder: Can't bind configuration
path 'sub.sample' due to IllegalArgumentException: Failed to getValue() with method
ru.vyarus.dropwizard.guice.yaml.support.FailedGetterConfig#getSample(0 params): null. Enable debug logs to
see complete stack trace or use @JsonIgnore on property getter.
Such warnings could be hidden by using @JsonIgnore
on property getter.
If this is not enough, or you need to avoid configuration introspection for other reasons, you can disable introspection completely with option:
GuiceBundle.builder()
.option(GuiceyOptions.BindConfigurationByPath, false)
...
When introspection disabled, only configuration object would be bound and
bindings by path would not be available. Note that even ConfigurationTree
object will not
contain configuration paths (option completely disables introspection mechanism).