Sharing properties between bootstrap configurations - spring

Problem:
I have two libraries whose purpose is to take properties from their respective sources and provide them to my application at startup. Currently both libraries provide their own PropertySourceLocators as a part of their BootstrapConfiguration.
(Just as described here https://cloud.spring.io/spring-cloud-static/spring-cloud-commons/1.3.3.RELEASE/multi/multi__spring_cloud_context_application_context_services.html#customizing-bootstrap-property-sources)
E.g.
libraryX is responsible for getting properties from SourceX and configuring a respective PropertySourceLocator.
libraryY is responsible for getting properties from SourceY and configuring a respective PropertySourceLocator
These libraries are configured at my bootstrap.yaml. E.g.
libX.enabled: true
libX.password: ${LIB_X_PASS}
libY.enabled: true
libY.password: ${LIB_Y_PASS}
Everything works great as long as these libraries don't use properties from one another.
Unfortunately, in my case libX should be a source of ${LIB_Y_PASS}.
So the bootstrap.yaml is now looking something like this:
libX.enabled: true
libX.password: ${LIB_X_PASS}
libY.enabled: true
libY.password: ${libX.values.lib_y_pass}
In this configuration application no longer starts since resolution of ${LIB_X_PASS} and ${libX.values.lib_y_pass} happens before PropertySourceLocators are called.
Potential solutions:
Use #PropertySource instead of PropertySourceLocator at libX.
Values provided by #PropertySources seem to be available at the moment when resolution of properties in bootstrap.yaml is happening. The only issue is, is that it seems to have the same priority with other application*.yaml/application*.properties files, located in the project. So it is impossible to override existing properties with something that will come from libX.
E.g.
libX.enabled: true
libX.password: ${LIB_X_PASS}
libY.enabled: true
libY.password: ${libX.values.lib_y_pass}
libX.values.lib_y_pass: dummy_password
libY.password will have dummy_password value instead of whatever comes from libX #PropertySource. Possible reason for that is the fact that bootstrap.yaml as propertySource comes before the property source libX created and during resolution spring picks the value from the first propertySource that has it.
Use org.springframework.boot.env.EnvironmentPostProcessor
We can inject libX property source as first property source during environment post processing phase.
E.g.
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
....
environment.getPropertySources().addFirst(libxPropertySource);
}
This seems to work fine and fixes the problem from the solution#1, but I don't know if there are hidden problems that I haven't found yet.
Question
Are there better ways to approach the problem?
It seems like either:
A) My case (when I need properties from one bootstrap PropertySourceLocator in another) goes against the design of PropertySourceLocator and in this case hacks like these is the only way.
B) I'm missing something and the problem can be solved in an much easier way.

Related

How to retrieve full endpoint URL in Quarkus test?

I am looking a solution to retrieve full endpoint URLs within Quarkus application to use it in tests to avoid hard-coding the paths.
The official guide suggests using the #TestHTTPEndpoint and #TestHTTPResource annotations.
If I annotate my test class with #TestHTTPEndpoint(MyResource::class), then all calls via RestAssured without specifying the path work just fine. The problem is, when I try to retrieve the endpoint URL like this (let's say, I need to call multiple endpoints in one test):
#TestHTTPEndpoint(MyResource::class)
#TestHTTPResource
lateinit var myResourceUrl: URL
it kind of works, but the injected URL does not include the quarkus.http.root-path value.
Instead of http://localhost:8081/root-path/my-resource I get just http://localhost:8081/my-resource.
Is there a way to retrieve a full endpoint path that includes the quarkus.http.root-path value?
Introduction
Let's consider the following versions as the current versions.
Quarkus: 2.11.2.Final.
Root cause analysis
The io.quarkus.test.common.http.TestHTTPResourceManager (TestHTTPResourceManager for short) class performs the value injection into the fields annotated with the io.quarkus.test.common.http.TestHTTPEndpoint (TestHTTPEndpoint for short) annotation:
The TestHTTPResourceManager class retrieves an instance of the org.eclipse.microprofile.config.Config (Config for short) from the org.eclipse.microprofile.config.ConfigProvider (ConfigProvider for short) class and uses the test.url configuration property value retrieved from the Config instance (the Config.getValue(…) method call) as the base URL.
The retrieved test.url configuration property value seems to correspond to the test.url system property value that was provided by the io.quarkus.test.common.http.TestHTTPConfigSourceProvider (TestHTTPConfigSourceProvider for short) class.
Possible root cause
For some reason the TestHTTPConfigSourceProvider class does not take into account the quarkus.http.root-path property value, when providing the test.url system property value, which seems to be used as the base URL: quarkus/TestHTTPConfigSourceProvider.java at 2.11.2.Final · quarkusio/quarkus:
static final String TEST_URL_VALUE = "http://${quarkus.http.host:localhost}:${quarkus.http.test-port:8081}${quarkus.servlet.context-path:}";
static final String TEST_URL_KEY = "test.url";
Therefore, it looks like a Quarkus issue: a defect (a bug) or a lack of a feature (or a lack of the feature implementation).
Therefore, it is worth reporting it as a Quarkus issue.
OK, so it seems to be a bug in Quarkus and will be fixed soon.
#TestHTTPResource annotation injects endpoint URL without the quarkus.http.root-path segment · Issue #27416 · quarkusio/quarkus · GitHub.
As a workaround, one could set the quarkus.servlet.context-path property in the test/resources/application.properties file like this:
quarkus.servlet.context-path=${quarkus.http.root-path}

Spring boot yaml property binding: collection types

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)

Configurable Component Scan

Is there a way to make the component scan configurable externally or through an intermediate resolver class? My requirement is that a common library should include one or more of other smaller facilities (each having their own controller, services etc.) depending on whether those are "configured" or needed - e.g. in application properties.
The closest I can see a possibility of designing this is to declare a #Configuration class in the common library and keep it in the component scan class path (always). In this class I need some way to say that the following are the allowed scan paths (based on how downstream projects have configured their application properties).
Seems like TypeFilter custom implementation should do it. But how do I read application properties from inside the type filter implementation (annotation takes only the .class, so Spring must be initializing it.
Any other ways? Thanks!
Regards,
Arnab.
This document describes how to create your own Auto-Configuration. It allows you to read properties and utilize several variations of #Conditional annotation.

How can I explicitly define an order in which Spring's out-of-the-box process of reading properties out of an available-in-classpath application.yml

UPDATE: I just published this question also here, I might have done a better work phrasing it there.
How can I explicitly define an order in which Spring's out-of-the-box process of reading properties out of an available-in-classpath application.yml will take place BEFORE my #Configuration annotated class which reads configuration data from zookeeper and places them as system properties which are later easily read and injected into members using #Value?
I have a #Configuration class, which defines a creation of a #Bean, in a which configuration data from zookeeper is read and placed as system properties, in a way that they can easily be read and injected into members using #Value.
#Profile("prod")
#Configuration
public class ZookeeperConfigurationReader {
#Value("${zookeeper.url}")
static String zkUrl;
#Bean
public static PropertySourcesPlaceholderConfigurer zkPropertySourcesPlaceHolderConfigurer() {
PropertySourcesConfigurerAdapter propertiesAdapter = new PropertySourcesConfigurerAdapter();
new ConfigurationBuilder().populateAdapterWithDataFromZk(propertiesAdapter);
return propertiesAdapter.getConfigurer();
}
public void populateAdapterWithDataFromZk(ConfigurerAdapter ca) {
...
}
}
Right now I pass the zookeeper.url into the executed program using a -Dzookeeper.url which is added to the execution line. Right now I read it by calling directly System.getProperty("zookeeper.url").
Since I'm using Spring-Boot application, I also have a application.yml configuration file.
I would like to be able to set the zookeeper.url in the application.yml, and keep my execution line clean as possible from explicit properties.
The mission turns out to be harder than I thought.
As you can see in the above code sniplet of ZookeeperConfigurationReader, I'm trying to inject that value using #Value("${zookeeper.url}") into a member in the class which performs the actual read of data from zookeeper, but at the time the code that needs that value accesses it, it is still null. The reason for that is that in spring life cycle wise, I'm still in the phase of "configuration" as I'm a #Configuration annotated class myself, and the spring's code which reads the application.yml data and places them as system properties, hasn't been executed yet.
So bottom line, what I'm looking for is a way to control the order and tell spring to first read application.yml into system properties, and then load ZookeeperConfigurationReader class.
You can try to use Spring Cloud Zookeeper. I posted a brief example of use here

Terracotta - Cannot cast to com.tc.object.bytecode.TransparentAccess

I have a rather large spring application, and all I'm trying to share is a single Map (using util.ConcurrentMap as implementation).
To do this, I created a bean in my appContext, and I tried to use the following tc-config line:
*/applicationContext.xml
Must I do something else to enable this to work? MyClass is a rather simple domain object that contains only primitives, two constructors, and accessors/mutators.
Must I do something else to get this working? I'm using Terracotta 3.0.0.
You need to create a tc-config.xml config file as described in http://www.terracotta.org/web/display/orgsite/Spring+Integration.

Resources