Plug into the hierarchy of Spring Boot property sources - java

I know the default order of spring's properties sources: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
how can i add my own property source with a specific precedence?
#PropertySource is not enough as it adds new properties with very low priority

There are plenty of ways to do this; I'll just quote the official documentation:
A SpringApplication has ApplicationListeners and ApplicationContextInitializers that are used to apply customizations to the context or environment. Spring Boot loads a number of such customizations for use internally from META-INF/spring.factories. There is more than one way to register additional ones:
Programmatically per application by calling the addListeners and addInitializers methods on SpringApplication before you run it.
Declaratively per application by setting context.initializer.classes or context.listener.classes.
Declaratively for all applications by adding a META-INF/spring.factories and packaging a jar file that the applications all use as a library.
The SpringApplication sends some special ApplicationEvents to the listeners (even some before the context is created), and then registers the listeners for events published by the ApplicationContext as well. See Section 23.4, “Application events and listeners” in the ‘Spring Boot features’ section for a complete list.
It is also possible to customize the Environment before the application context is refreshed using EnvironmentPostProcessor. Each implementation should be registered in META-INF/spring.factories:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor
My way was always to add an ApplicationEnvironmentPreparedEvent listener:
public class IntegrationTestBootstrapApplicationListener implements
ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 4;
public static final String PROPERTY_SOURCE_NAME = "integrationTestProps";
private int order = DEFAULT_ORDER;
public void setOrder(int order) {
this.order = order;
}
#Override
public int getOrder() {
return this.order;
}
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
if (!environment.getPropertySources().contains(PROPERTY_SOURCE_NAME)) {
Map<String, Object> properties = ...; // generate the values
// use whatever precedence you want - first, last, before, after
environment.getPropertySources().addLast(
new MapPropertySource(PROPERTY_SOURCE_NAME, properties));
}
}
}
But you can just as easily use an initializer:
public class IntegrationTestBootstrapApplicationListener implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final String PROPERTY_SOURCE_NAME = "integrationTestProps";
#Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> properties = ...; // generate the values
// use whatever precedence you want - first, last, before, after
environment.getPropertySources().addLast(
new MapPropertySource(PROPERTY_SOURCE_NAME, properties));
}
}

Related

How to add SynchronizationCallbacks to #TransactionalEventListener during spring boot application startup?

I have a spring boot application that uses a few #TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT). I noticed that spring boot doesn't do any exception logging for them when they end up with an exception being thrown.
Because of this I wanted to add some generic logging facility for such exceptions. I found that TransactionalApplicationListener.SynchronizationCallback is the interface I need to implement. However it seems complicated to register these callbacks. I didn't find any call of TransactionalApplicationListener#addCallback in the spring dependencies that would achieve this.
Trying to get a list of TransactionalApplicationListener and the SynchronizationCallback injected and then call addCallback in a #PostConstruct didn't get me further because there were always no listeners injected even though the application did make successful use of them.
So how do I add SynchronizationCallbacks to TransactionalApplicationListeners during spring boot application startup?
The first thing to note is that TransactionalApplicationListeners like all ApplicationListener are not beans in the spring context. They live somewhat outside of it (see org.springframework.context.ConfigurableApplicationContext#addApplicationListener). So injecting them is not possible for the application context.
While debugging and looking through spring sources one finds that these listeners are being created by org.springframework.transaction.event.TransactionalEventListenerFactory. And that is where my solution steps into. We decorate that factory with another one that is aware of SynchronizationCallbacks:
public class SynchronizationCallbackAwareFactory implements EventListenerFactory, Ordered {
private final TransactionalEventListenerFactory delegate;
private final Provider<List<SynchronizationCallback>> synchronizationCallbacks;
private final int order;
public SynchronizationCallbackAwareFactory(TransactionalEventListenerFactory transactionalEventListenerFactory,
Provider<List<SynchronizationCallback>> synchronizationCallbacks,
int order) {
this.delegate = transactionalEventListenerFactory;
this.synchronizationCallbacks = synchronizationCallbacks;
this.order = order;
}
#Override
public boolean supportsMethod(Method method) {
return delegate.supportsMethod(method);
}
#Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
ApplicationListener<?> applicationListener = delegate.createApplicationListener(beanName, type, method);
if (applicationListener instanceof TransactionalApplicationListener) {
TransactionalApplicationListener<?> listener = (TransactionalApplicationListener<?>) applicationListener;
Collection<SynchronizationCallback> callbacks = this.synchronizationCallbacks.get();
callbacks.forEach(listener::addCallback);
}
return applicationListener;
}
#Override
public int getOrder() {
return order;
}
}
Note that I use a javax.inject.Provider in my case to make the retrieval of the callbacks at the latest possible time.
The decorator has to be Ordered because spring will use the first factory supporting the method it gets across. And therefore the order of an instance of this class has to have higher precedence as the order value 50 of TransactionEventListenerFactory.
I had simmilar problem with code as below
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class SomeListenerFacade {
#TransactionalEventListener
public void onSomething(SomeEvent event) {
throw new RuntimeException("some cause");
}
}
I followed your solution. It worked. On the way I've found an alternative way for at least seeing that exception in the logfile
# application.properties
logging.level.org.springframework.transaction.support.TransactionSynchronizationUtils = DEBUG

How to find unused properties in a spring boot 1 application

Is there a way in a spring boot 1 application to know the effective properties used by a Spring Boot application versus all properties setted up with YAML, system properties...
Ideally I would like to detect that in an ApplicationListener class and prevent the application to start if there are any obsolete properties that we maintain in our framework.
Thx by advance for your help,
Eric
What I did when I had the same requirement was creating my own PropertyPlaceholderConfigurer:
public class DisplayablePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private static final Logger log = LoggerFactory.getLogger(DisplayablePropertyPlaceholderConfigurer.class);
private int processedNum;
private HashSet<String> propertyNames = new HashSet<>();
#Override
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) {
super.doProcessProperties(beanFactoryToProcess, valueResolver);
}
#Override
protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
return super.resolvePlaceholder(placeholder, props, systemPropertiesMode);
}
#Override
protected String resolvePlaceholder(String placeholder, Properties props) {
propertyNames.add(placeholder);
return super.resolvePlaceholder(placeholder, props);
}
#Override
protected String resolveSystemProperty(String key) {
return super.resolveSystemProperty(key);
}
public HashSet<String> getPropertyNames() {
return propertyNames;
}
}
You could then register some CommandLineListener or ApplicationEvent listener to call getPropertyNames() on application startup.
After that you will get a list of used properties. It's a good point to start, isn't it? You can sort both lists and compare to filter out properties that are not used.

Google Guice Properties Management

I would like to create a proper properties management strategy in a java webapp that relays on google guice as a DI framework.
I would like to have a mechanism answering the following 3 requirements:
I would like to be able to inject properties using guice (#Named)
I would like to be able to access properties in a static way
The mechanism should support prioritization of properties, meaning that a property can be wrapped in the deployed war with a certain value but it can also be redundant in the target system level or local file system (of the target machine I deploy on), in such a case the value in the war will be overridden by the value that exists in the target machine.
I believe this is a standard requirement. Now, using guice standard binder I can easily get the first requirement but not the other two. To get the other two I created my own class that does the following:
Wraps and exposes the binding methods of guice (those that binds properties) For example:
public static void bindString(AnnotatedBindingBuilder<String> binder, String property, String defaultValue) {
binder.annotatedWith(Names.named(property)).toInstance(getProperty(property, defaultValue));
}
Where the getProperty method knows how to handle my properties (get the value from the war or system level) and exposes the properties statically as well.
So basically as long as I'm using this utility that I created for properties bindings I'm good, it covers all my requirements but once I use the standard guice bindings I'm losing the second and third requirement.
Is there a way to override guice bindings and get all those 3 requirements?
Once I had the same challange in a spring based app and was pretty easy. I implemented ApplicationContextInitializer with the following method:
#Override
public void initialize(ConfigurableWebApplicationContext ctx) {
PropertySource<Map<String, Object>> localProps = null;
try {
localProps = new ResourcePropertySource(new ClassPathResource(LOCAL_PROPERTIES_FILE_NAME));
} catch (IOException e) {
LOG.fatal("Could not load local properties from classpath " + LOCAL_PROPERTIES_FILE_NAME);
return;
}
LOG.info("Loaded configuration from classpath local file " + LOCAL_PROPERTIES_FILE_NAME);
ctx.getEnvironment().getPropertySources().addFirst(localProps);
}
so this gave me a way to add local properties with highest priority to my Environment. In case of overlap with war properties the local ones had higher priority. In addition I exposed my Environment statically so I has static access to my properties (for services that are not managed by the container, legacy mostly).
How can I achieve this with guice?
Unfortunately, I don't think that you are going to find anything that gives you a truly clean and satisfying implementation. Especially, I don't think that you will find anything that gives you exactly what you want without implementing at least portions of it yourself.
If I had those needs, I would make sure that my injector is created in a central InjectorFactory. If you require a large number of parameters from outside to create your injector, I would simply create it once at the very beginning of my application and then cache the injector into a static final field. This would make it available to a static method. I would bind my "fall-back" property loading to an explicit provider. That way, instead of using the standard Names.bindProperties(...) method, I would bind it directly to a Provider. This provider then implements the logic that is necessary to perform the fallback or to merge multiple property files. Having the injector cached to a static field means that I can call a static method to access properties from a global-context outside of my injected classes.
Using your own provider seems initially unpleasant, but can provide some additional benefits. For starters, you can implement your fallback strategy exactly how you want. Additionally, you can add additional behaviors such as auto-reloading your property files, etc (not shown in my code sample).
public class InjectorFactory {
private static Injector injector = null;
public static synchronized Injector getOrCreateInjector() {
if(injector == null) {
injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
Properties properties1 = createProperties("file1.properties");
Properties properties2 = createProperties("file2.properties");
Set<Object> propertyNames = new HashSet<Object>();
propertyNames.addAll(properties1.keySet());
propertyNames.addAll(properties2.keySet());
for (Object object : propertyNames) {
String propertyName = (String) object;
bind(String.class).annotatedWith(Names.named(propertyName)).toProvider(new StringProvider(properties1, properties2, propertyName));
}
}
private Properties createProperties(String propertyFileName) {
try {
InputStream stream = InjectorFactory.class.getResourceAsStream(propertyFileName);
try {
Properties properties = new Properties();
properties.load(stream);
return properties;
} finally {
stream.close();
}
} catch (IOException exception) {
throw new RuntimeException("Could not load properties file");
}
}
});
}
return injector;
}
public static String getProperty(String propertyName) {
return getOrCreateInjector().getInstance(Key.get(String.class, Names.named(propertyName)));
}
}
Given the above code and file1.properties:
property1=Property1Value
property2=Property2Value
And file.properties:
property2=IncorrectProperty2Value
property3=Property3Value
with the provider
public class StringProvider implements Provider<String> {
private Properties properties1;
private Properties properties2;
private String propertyName;
public StringProvider(Properties properties1, Properties properties2,
String propertyName) {
this.properties1 = properties1;
this.properties2 = properties2;
this.propertyName = propertyName;
}
public String get() {
if(properties1.containsKey(propertyName)) {
return properties1.getProperty(propertyName);
}
return properties2.getProperty(propertyName);
}
}
The following usage:
public class InjectorFactoryTest {
public static void main(String ... parameters) {
System.out.println(InjectorFactory.getProperty("property1"));
System.out.println(InjectorFactory.getProperty("property2"));
System.out.println(InjectorFactory.getProperty("property3"));
}
}
Outputs:
Property1Value
Property2Value
Property3Value

Is it possible to use the Multibinder within Guice to construct dynamic sets based on application configuration?

I have an application which relies on Properties configuration to determine whether to mix in various components or not.
For example, the configuration has boolean flags like "componentX.enabled" etc which determine whether these components should be active or not.
Currently I am using these flags in my provider methods like so:
#Provides
#Singleton
#Nullable
public ComponentX provideComponentX(Properties props) {
if (props.isComponentXEnabled()) {
return new ComponentX();
} else {
return null;
}
}
#Provides
#Singleton
public Set<Component> provideComponentSet(
#Nullable ComponentX compX,
ComponentY compY,
ComponentZ compZ
) {
Set<Component> comps = new HashSet<>();
if (compX != null) {
comps.add(compX);
}
comps.add(compY);
comps.add(compZ);
return comps;
}
This approach seems a little clunky (it relies on possible injecting null)- but is there a better way?
The only other way I can think of doing it is by using a parent injector to obtain the application Properties into my module, and then using the set Multibinder.
Then use the create child injector with the new module to complete the bootstrap process.
public class Module extends AbstractModule {
Properties props;
public Module(Properties props) {
this.props = props;
}
public void configure() {
Multibinder<Component> compBinder = Multibinder.newSetBinder(binder(), Component.class);
if (props.isComponentXEnabled()) {
compBinder.addBinding().to(ComponentX.class);
}
compBinder.addBinding().to(ComponentY.class);
compBinder.addBinding().to(ComponentZ.class);
}
}
This also seems a little clunky because it requires the use of a child injector etc.
Again, is there a better way?
Maybe I could use Netflix's Governator (https://github.com/Netflix/governator/wiki/Configuration-Mapping) to inject Configuration values into my module (not sure if that is possible or not)?
How do other people approach this problem?
The applications I've been working with recently have a properties file (or other configuration) that is used to decide which parts of the application are relevant. Our typical approach is parse those properties immediately (just to a Properties object) and construct the application module(s) from that, and they will then conditionally include other modules based on the values specified.
In a couple of places, this has grown into an "init parameters" set, with an enumeration of possible parameters:
enum InitParam {
PricesQueue("prices.queue")
}
Each enum instance is related to a property key and there is a method to get a basic string value for each parameter from Properties:
boolean presentIn(Properties props) { return props.containsKey(propertyKey); }
String valueIn(Properties props) { return props.getProperty(propertyKey); }
So this can be used like so:
public AppModule extends AbstractModule {
private final Properties config;
protected void configure() {
if (InitParam.PricesQueue.presentIn(config)) {
install(new PricesQueueConsumerModule(config));
}
}
}
Additionally, there is a module to bind all the values in the config properties to String, Optional<String> etc, allowing:
#Inject
public PricesQueueConsumer(#FromInitParam(InitParam.PricesQueue) String queueName) {
}
This will trap the queue consumer being referenced when the configuration isn't available (the module won't bind a string if the value isn't present in the config file) while still allowing the behaviour for when the value isn't present to be deferred to later (by injecting Optional<String> instead)
So this is somewhat similar to your second approach, except that I'd not considered the using-Guice-to-inject-Guice-modules approach, which seems a bit convoluted. Although probably it's essentially the same. Maybe rather than a parent/child injector you could simply create a "bootstrapping" injector to build your top-level application module, and then use that to build a completely separate injector?

Can I manually load #ConfigurationProperties without the Spring AppContext?

Is there any way to load a class marked with #ConfigurationProperties without using a Spring Context directly? Basically I want to reuse all the smart logic that Spring does but for a bean I manually instantiate outside of the Spring lifecycle.
I have a bean that loads happily in Spring (Boot) and I can inject this into my other Service beans:
#ConfigurationProperties(prefix="my")
public class MySettings {
String property1;
File property2;
}
See the spring docco for more info http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-command-line-args
But now I need to access this bean from a class that is created outside of Spring (by Hibernate). The class is created so early in the app startup process that Spring Boot has not yet made the application context available through the classic lookup helper methods or roll-my-own static references.
So I instead want to do something like:
MySettings mySettings = new MySettings();
SpringPropertyLoadingMagicClass loader = new SpringPropertyLoadingMagicClass();
loader.populatePropertyValues(mySettings);
And have MySettings end up with all its values loaded, from the command line, system properties, app.properties, etc. Is there some class in Spring that does something like this or is it all too interwoven with the application context?
Obviously I could just load the Properties file myself, but I really want to keep Spring Boot's logic around using command line variables (e.g. --my.property1=xxx), or system variables, or application.properties or even a yaml file, as well as its logic around relaxed binding and type conversion (e.g. property2 is a File) so that it all works exactly the same as when used in the Spring context.
Possible or pipe dream?
Thanks for your help!
I had the same "issue".
Here is how I solved it in SpringBoot version 1.3.xxx and 1.4.1.
Let's say we have the following yaml configuration file:
foo:
apis:
-
name: Happy Api
path: /happyApi.json?v=bar
-
name: Grumpy Api
path: /grumpyApi.json?v=grrr
and we have the following ConfigurationProperties:
#ConfigurationProperties(prefix = "foo")
public class ApisProperties {
private List<ApiPath> apis = Lists.newArrayList();
public ApisProperties() {
}
public List<ApiPath> getApis() {
return apis;
}
public static class ApiPath {
private String name;
private String path;
public String getName() {
return name;
}
public void setName(final String aName) {
name = aName;
}
public String getPath() {
return path;
}
public void setPath(final String aPath) {
path = aPath;
}
}
}
Then, to do the "magic" things of Spring Boot programmatically (e.g. loading some properties in a static method), you can do:
private static ApisProperties apiProperties() {
try {
ClassPathResource resource;
resource = new ClassPathResource("/config/application.yml");
YamlPropertiesFactoryBean factoryBean;
factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setSingleton(true); // optional depends on your use-case
factoryBean.setResources(resource);
Properties properties;
properties = factoryBean.getObject();
MutablePropertySources propertySources;
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
ApisProperties apisProperties;
apisProperties = new ApisProperties();
PropertiesConfigurationFactory<ApisProperties> configurationFactory;
configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the #ConfigurationProperties
configurationFactory.bindPropertiesToTarget();
return apisProperties; // apiProperties are fed with the values defined in the application.yaml
} catch (BindException e) {
throw new IllegalArgumentException(e);
}
}
Here's an update to ctranxuan's answer for Spring Boot 2.x. In our situation, we avoid spinning up a Spring context for unit tests, but do like to test our configuration classes (which is called AppConfig in this example, and its settings are prefixed by app):
public class AppConfigTest {
private static AppConfig config;
#BeforeClass
public static void init() {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new ClassPathResource("application.yaml"));
Properties properties = factoryBean.getObject();
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(propertySource);
config = binder.bind("app", AppConfig.class).get(); // same prefix as #ConfigurationProperties
}
}
The "magic" class you are looking for is PropertiesConfigurationFactory. But I would question your need for it - if you only need to bind once, then Spring should be able to do it for you, and if you have lifecycle issues it would be better to address those (in case they break something else).
This post is going into similar direction but extends the last answer with also validation and property placeholder resolutions.
Spring Boot Binder API support for #Value Annotations
#Value annotations in ConfigurationPropertys don't seem to bind properly though (at least if the referenced values are not part of the ConfigurationProperty's prefix namespace).
import org.springframework.boot.context.properties.bind.Binder
val binder = Binder.get(environment)
binder.bind(prefix, MySettings.class).get

Categories

Resources