Spring boot yaml property binding: collection types - spring

I find Spring Boot's (or spring in general) handling of yaml collections to be a bit peculiar. Collections according to yaml specs should be written in .yaml files as:
myCollection: ['foo', 'bar']
or
myCollection:
- foo
- bar
But neither #Value("${myCollection}") annotation or Environment.getProperty("myCollection", String[].class) (also tried List.class) can read collection properties (returns null). The only method I know of that works is to use #ConfigurationProperties annotation described in spring boot docs.
The problem with #ConfigurationProperties annotation is that (a) it is too verbose if all I want is a single property and (b) it rely on bean injection to get an instance of the #ConfigurationProperties class. Under some circumstances, bean injection is not available and all we have is a reference to Environment (e.g: thru ApplicationContext).
In my particular case, I want to read some properties during ApplicationEnvironmentPreparedEvent event, since it happens before context is built, the listener has to be manually registered and therefore, no bean injection. Via the event argument, I can get a reference to Environment. So, I can read other properties but cannot read collections.
A couple of "solutions" I noted (quoted because I don't find them very satisfactory):
Specify collections in .yaml file as myCollection: foo, bar. But this is not ideal because, the format isn't really yaml anymore.
Read individual elements using an index, for example Environment.getProperty("myCollection[0]", String.class). Will require some not-so-elegant utility methods to read and put all elements into a List.
So, my questions is - What is a good way to read collection-type properties if I cannot use #ConfigurationProperties? Also curious why comma-separated format works but not yaml-style collections.
EDIT: corrected some typos

Quite Frankly Spring boot application.properties and application.yaml or application.yml is meant to load configuration properties.
The #ConfigurationProperties annotation is designed as an abstraction to hide the implementations of configuration properties and support both .properties and .yaml/.yml.
For yaml/yml however Spring uses org.yaml.snakeyaml.Yaml library underneath to parse the file and load it to a Properties object inside org.springframework.boot.env.YamlPropertySourceLoader and a Collection is mapped as a Set not an array or List. So you try doing the following;
Environment.getProperty("myCollection", Set.class)

Related

When using ConfigurationProperties with a camelCase prefix, how do I solve "Prefix must be in canonical form"?

I had a property configured in my yml as
foobar:
baz: 7
and a configuration class annotated with
#ConfigurationProperties(prefix = "foobar")
and everything was working fine.
The code in my organization is generally camelCase, so I renamed both the property and prefix to fooBar. IntelliJ is now highlighting the prefix = "foobar" line with the error, "Prefix must be in canonical form". What can I do, while keeping camelCasing in the yml config?
Spring Boot supports multiple formats of property names, but encourages you to access them in a canonical way.
Per Property Binding in Spring Boot 2.0:
It turns out the idea of relaxed property names is much easier to implement if you restrict it to one direction. You should always access properties in code using a canonical form, regardless of how they are represented in the underlying source.
The ConfigurationPropertyName class enforces these canonical naming rules, which basically boil down to “use lowercase kebab-case names”.
So, for example, you should refer to a property in code as person.first-name even if person.firstName or PERSON_FIRSTNAME is used in the underlying source.
You can keep your config yml in camel case:
fooBar:
baz: 7
but change the access in the configuration class annotation to use kebab-case:
#ConfigurationProperties(prefix = "foo-bar")

How to map service factory PIDs to their `ObjectClassDefinition`

In OSGi R6 I desire to programmatically validate user-supplied String configuration properties plus a service factory PID against what is supported by whatever configurable #Component (or ManagedServiceFactory) that declares it configures this PID, e.g. #Component(configurationPid=some.service.factory.pid, ...). Additionally, I want to somehow convert valid String properties to their appropriate property types. Looking through the OSGi Compendium, it seems the Metatype Service is what I'm looking for.
If that's correct, given the following:
Applicable components uses component property types to specify their configuration
Component property types are annotated with #ObjectClassDefinition
Components are annotated with #Designate, mapping it to the applicable #ObjectClassDefinition
Is this the most straightfoward way to map factory PIDs to their ObjectClassDefinition:
Call BundleContext.getBundles(). For each bundle, call MetaTypeService.getMetaTypeInformation(Bundle).
For each returned MetaTypeInformation call MetaTypeInformation.getFactoryPids() and filter on the factory PIDs I care about.
For applicable MetaTypeInformation, call MetaTypeInformation.getObjectClassDefinition(String, String) to obtain the ObjectClassDefinition, using either a default or specific locale.
(Tangential, the above seems expensive to perform each time, so caching bundle IDs, mapping them to associated factory PIDs, and keeping the cache up-to-date somehow seems appropriate.)
Or, is there some other OSGi magic that can be programmatically queried with a service factory PID, which returns something that gets to some ObjectClassDefinition quicker than the above process?
Update 1
Stepping back, I'm writing a CRUD-wrapper around ConfigurationAdmin for each of my configurable components. I'm trying to avoid createFoo, deleteFoo, updateFoo, createBar, ... My application happens to be amenable to URIs. So my working approach was to use Metatype Service, pass in a parsed URI query (Map<String, List<String>>), and then utilize Metatype Service to validate and reconstruct these values, circling back to the OP. (On the side, seems like a not-pretty hack to me.)
Another approach was to use aQute.bnd.annotations.metatype.Configurable.createConfigurable(Class, Map), which I preferred more! Until I saw this bnd GitHub comment:
The bnd metatype annotations are deprecated in bnd 3.2 and will be removed in bnd 4.0. These annotations are replaced by the OSGi metatype annotations.
So I didn't want to rely on that package if it's going away soon. I looked at what Felix does and didn't want to use their equivalent Configurable class. I'm all ears on different approaches!
Update 2
Reducing this more, I'd like to validate potentially user-supplied key/values configuration properties to ensure they're applicable for some configuration pid, prior to calling ConfigurationAdmin.createFactoryConfig. Maybe this is overkill?
I once created a class that takes the configuration class, creates a proxy, and then uses this proxy to get the name of the method and the type. It was used something like this:
ConfigHelper<Config> helper = new ConfigHelper( Config.class, "my.pid");
int port = helper.get().port(); // get the configuration
helper.set( helper.get().port(), 1000);
helper.update();
The proxy you get from the get would record the method when one of the methods is called. On the set method it would use the last called proxy method to identify the property. It would then convert the given value to the property type based on the method's return value. The bnd converter is ideal for this but I think Felix now has a standard OSGi converter. (Which is based on the ideas of the bnd converter.)
The method name is then used as the property. The name mangling necessary is defined in an OSGi spec. This allows you to use underscores, Java keywords, and dotted names.
So this would allow you to roundtrip configurations. No worry about the types, they will automatically fall in their place.
Updated This is updated after I understood the question better
Updated 2 Added an example at https://github.com/aQute-os/biz.aQute.osgi.util/tree/master/biz.aQute.osgi.configuration.util

Spring bean set values taken from properties file

I have a spring bean defined like
<util:set id="siteLanguages" value-type="java.util.Locale" set-class="java.util.LinkedHashSet">
<value>#{T(java.util.Locale).GERMAN}</value>
<value>#{T(java.util.Locale).ITALIAN}</value>
<value>#{T(java.util.Locale).ENGLISH}</value>
</util:set>
I'm wondering how can I pass the value list definitions to the bean as a properties list value.
<util:set id="siteLanguages" value-type="java.util.Locale" set-class="java.util.LinkedHashSet">
???
</util:set>
I would like to have in my properties file something like
site.languages=#{T(java.util.Locale).GERMAN},#{T(java.util.Locale).ITALIAN},#{T(java.util.Locale).ENGLISH}
or even better
site.languages=GERMAN,ITALIAN,ENGLISH
and pass this in to the bean
The main problem is that you cannot express multivalue data structures (arrays, lists etc) in plain java property files by using the java standard api.
You can do it easily though with Apache commons configuration library.
http://commons.apache.org/proper/commons-configuration/

Custom annotation like #Value

I need to create a means to add a custom annotation like
#Value("${my.property}")
However, in my case I need to get the value from a database rather then a properties file.
Basically I would like to create a bean on container startup that reads in property name value pairs from a database and can then inject these into fields belonging to other beans.
Approach #1:
One way is to create an Aspect, with a point-cut expression that matches any method having this annotation.
Your aspect will then:
Read the property value in the annotation
Look up the required value an inject it into the class.
AOP Kickstart
Here's a guide to getting started with AOP in Spring
http://www.tutorialspoint.com/spring/aop_with_spring.htm
Joinpoint matching
Here's a reference that describes how to create a join-point that matches on annotations: http://eclipse.org/aspectj/doc/next/adk15notebook/annotations-pointcuts-and-advice.html
Approach #2:
Another way is to use a BeanFactoryPostProcessor - this is essentially how a PropertyPlaceholderConfigurer works.
It will look at your bean definitions, and fetch the underlying class.
It will then check for the annotation in the class, using reflection.
It will update the bean definition to include injecting the property as per the value in the annotation.
. . actually I think approach #2 sounds more like what you want - all of the processing happens on "start-up". . . (In actual fact your modifying the bean recipes even before startup). . whereas if you used AOP, you'd be intercepting method invocations, which might be too late for you?
Namespace Handler
If you wanted you could even create your own Spring namespace handler to turn on your post processor in a terse way. Eg:
<myApp:injectFromDb />
as an alternative to:
<bean class="MyDatabaseLookupProcessorImpl etc, etc. />
Update: Approach #3
As of Spring 3.1 there's also the PropertySourcesPlaceholderConfigurer, that will provide most of the plumbing for you, so you can achieve this with less code.
Alternatively you should be able to configure kind of properties repository bean and then use it in SpEL directly in #Value annotation.
Let's say you'd have bean called propertiesRepository in your context that implements following interface:
interface PropertiesRepository {
String getProperty(String propertyName);
}
then on bean where you want to inject values you can use following expression
#Value("#{propertiesRepository.getProperty('my.property')}")
String myProperty;
You can use #Value annotation by injecting database configuration in application environment itself.
I know this is an old question but I didn't find an exact solution. So documenting it here.
I have already answered the same on different forum.
Please refer to this answer for exact solution to your problem.

Specifing part of Spring context properties from JUnit test files

I have multiple mock implementations of a bean, that bean is referenced as beanA. Mock's reference names is let say: beanAMock1, beanAMock2... beanAMockN.
In context file I am aliasing beanA reference according to context placeholder beanAImplementation:
<alias name="${beanAImplementation}" alias="beanA"/>
Also there other beans (beanB, beanC...) that have mock implementations like this. And I have a number test suits that utilizing theirs specific sets of beanA, beanB... implementations.
All beans have default values for theirs switching placeholders that is specified in property file included from context.
I am looking for a ways to change part of bean's implementations per test suit.
I know three ways to do this:
1) create additional context file for each test suit that will load required properties from separate property files - this way requires creation of two additional files per test suite (context, property).
2) create profile for each test suite with aliases definitions. As I understand this requires providing aliases for each bean within per each profile - while I am having a default set of beans implementations.
3) create context within test suite manually and override required properties - with this I need to write many of the code within test suite.
Is there another ways? (may be like #3 but working with SpringJUnit4ClassRunner or its subclasses and if it will be possible just to provide required properties within annotations - it seems to be a best approach)
I've solved this problem by subclassing SpringJUnit4ClassRunner and overriding its
TestContextManager createTestContextManager(Class clazz) method. This method accepts Class object of test suite and is called before context creation. So it is possible to read additional annotations from test suite class and set system properties that will substitute corresponding placeholders during context creation.

Resources