Spring: Using PropertyPlaceholderConfigurer support outside of application context - java

My applications requirements mean that I need to create application beans manually at runtime and then add them to the application context.
These beans belong to third party libraries so I cannot modify them e.g. a TibjmsConnectionFactory
So my factory class that creates these beans needs to be provided with a Properties object that will set username, password, connectionTimeouts etc
Ideally I'd like to use Spring's property support so I do not need to convert Strings to Integers etc
Also, the Properties provided to my factory class will not be the same Properties used for by the PropertyPlaceholderConfigurer in my overall ApplicationContext
How do I achieve this, or is it even possible?
public class MyCustomFactoryStrategy {
#Override
public TibjmsConnectionFactory create(Properties properties) {
TibjmsConnectionFactory connectionFactory = new TibjmsConnectionFactory();
connectionFactory.setServerUrl(properties.getProperty("emsServerUrl")); // this is a string
connectionFactory.setConnAttemptCount(new Integer(properties.getProperty("connAttemptCount"))); // this is an integer
...
return connectionFactory;
}

Have a look in this post. I think it might be what you need.
[PropertyPlaceholderConfigurer not loading programmatically
[1]:

Related

Is it possible to use #ConstructorBinding bound immutable #ConfigurationProperties beans in an implementation of EnvironmentPostProcessor?

Is it possible to use #ConstructorBinding bound immutable #ConfigurationProperties beans in an implementation of EnvironmentPostProcessor?
I have an EnvironmentPostProcessor implementation:
#Order(LOWEST_PRECEDENCE)
#EnableConfigurationProperties({KeyVaultCertificatesEnvironmentProperties.class, KeyVaultCertificatesEnvironmentServerSslProperties.class})
public class KeyVaultCertificatesEnvironmentPostProcessor implements EnvironmentPostProcessor {
private KeyVaultCertificatesEnvironmentProperties keyVaultProperites;
private KeyVaultCertificatesEnvironmentServerSslProperties sslProperties;
This implementation must use some #ConfigurationProperties beans. I would like these bean to be immutable. Here is a sketch of the beans
#ConstructorBinding
#ConfigurationProperties(prefix=KeyVaultCertificatesEnvironmentProperties.PREFIX)
public class KeyVaultCertificatesEnvironmentProperties {
//...
}
#ConstructorBinding
#ConfigurationProperties(prefix=KeyVaultCertificatesEnvironmentServerSslProperties.PREFIX)
public class KeyVaultCertificatesEnvironmentServerSslProperties {
//...
}
I don't know how to make it so the two #ConfigurationProperties beans are available to my EnvironmentPostProcessor.postProcessEnvironment implementation. I suspect there may be some timing issues.
Any ideas how I can do this?
Thanks,
Ed
It's not possible to directly use any #ConfigurationProperties beans with an EnvironmentPostProcessor because the ApplicationContext that holds the beans is always created after the Environment. It is, however, possible to use the org.springframework.boot.context.properties.bind.Binder class directly if you want to create a an object from the properties that have been loaded into the Environment so far.
For example, you can have the following class:
public class Certs {
// ... fields
Certs(String first, String second) {
// ...
}
}
and bind it with the following code:
Binder binder = Binder.get(environment);
Certs certs = binder.bind("my.certs", Certs.class).get();
Note that the Certs properties class doesn't use any #ConfigurationProperties or #ConstructorBinding annotations and it has a single constructor. For more complex setups you might need to use the Binder constructor that accepts a BindConstructorProvider.
If you're using Spring Boot 2.4 or above you might want to look into the ConfigDataLocationResolver and ConfigDataLoaders classes. These allow you to support custom imports from an application.properties or application.yaml file. The resolver provides a ConfigDataLocationResolverContext object with a getBinder() method that you can use for reading additional properties. You can read a bit about it in this blog post.
You can also look at the new org.springframework.boot.Bootstrapper interface that was added in Spring Boot 2.4 to help Spring Cloud solve a similar chicken-and-egg problem. There's also a test application in the Spring Boot codebase that might help.

What's the Spring way of accessing properties from an entity?

I'm new to Spring and I'm building an application where some entities (JPA/Hibernate) need access to a property from application.properties. I do have a configuration class in which this is trivial:
#Configuration
public class FactoryBeanAppConfig {
#Value("${aws.accessKeyId}")
private String awsAccessKeyId;
#Value("${aws.secretKey}")
private String awsSecretKey;
}
but since entities do not have and I think they should not have the annotations such as #Configuration or #Component, what's the Spring way for them to access the property?
Now, I know I can create my own class, my own bean, and make it as a simple wrapper around the properties; but is that the Spring way to do it or is there another way?
specify Property file location using #PropertySource
Something like below
#PropertySource("classpath:/application.proerties")
You also need to add below bean in your config
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
There is no "Spring way", since JPA entities and Spring have nothing to do with each other. Most importantly, JPA entities are not Spring beans, so Spring doesn't know or care about them as they're not managed by Spring.
You can try to hack around, trying in vain to access Spring's configuration from code that should not be trying to access it, or you can accept the truth that your design is broken and you're trying to do something that's not meant to be done.
As was proposed several times, use a service class for this. It's managed by Spring, so it can access the Spring config, and it can handle entities, so there's no crossing boundaries.
First create a public static variable in some bean managed by Spring, then make the following use of the #Value annotation.
public static String variable;
#Value("${variable}")
private void setVariable(String value) {
variable = value;
}
It will be set at runtime on startup, now you can access it from entities and everywhere else because it is just a public static var.
You can use #PropertySource to load the properties file as follows
#Configuration
#PropertySource("classpath:/com/organization/config/application.proerties")
public class FactoryBeanAppConfig {
...
}
Entities should not acces environment properties. If you are using your entity through a service, then the service can access the properties to act on the entity.

Using java annotation with dynamic parameter

What I'm trying to achieve is have a #Resource with a dynamic name parameter. Specifically, I want to inject a DataSource object using #Resource(name = "{JNDI_NAME_PARAM}") because we can have many datasources configured in an application server, and the datasource used by the application is defined in an .xml or .config file. Since I do not know the name of the datasource during compile time I need to be able to get it at runtime. Right now I'm injecting a custom #ApplicationScoped bean which creates a datasource in its #PostConstruct method using InitialContext().lookup(). However I'm curious (mostly because it is more elegant) as to how I could achieve injection using the #Resource annotation.
I COULD create a custom default JNDI name in the app server and change the datasource it points to when needed but this can't work with more than one deployment and many times we have the application deployed twice, once in a test database and once in a production database so having the JNDI point at two different datasources at the same time.
You can use the Method based injection.
It requires the setter method (setMyDB).
public class Test {
public javax.sql.DataSource myDB;
#Resource(name="student")
private void setMyDB(javax.sql.DataSource ds) {
myDB = ds;
}
}
If the names are known, we can have multiple resources under
#Resources({
#Resource(your type)
#Resource(your type)
})

Can a Spring FactoryBean get access to all of the properties in the context?

I'm trying to write a Spring FactoryBean that will produce a list of Request objects. The number of requests and the values that go into them are configurable at runtime, so I want to use properties for these.
Each request comprises of a pair of ID values, so I need some way of providing the Factory Bean with a configurable list of these ID pairs (Call them A and B for now).
What I've got so far is to use a property that looks something like:
requests=1/2,3/4,5/6
which then defines three requests, one with A=1 and B=2, one with A=3 and B=4, and one with A=5 and B=6.
This is obviously a bit nasty to configure, and rather prone to errors. What would be much nicer would be to do something with the values split out over many properties, so the above could be something like:
requests.1.A=1
requests.1.B=2
requests.2.A=3
requests.2.B=4
requests.3.A=5
requests.3.B=6
Which just makes it a bit more obvious what is going on.
However, I can't find any way of having my FactoryBean configured to access all of the available properties, instead of just the specifically named property that is passed in from the context.
Am I missing something here? Or - even better - is there a better way of doing this kind of config that is easier supported and maintained?
You can inject an Environment bean into your FactoryBean instance, it is provided by the context and you do not have to configure it. I am not sure how you are configuring your beans, but I always favor Java config. So this example will use Java config.
#Configuration
class FactoryBeanConfig {
#Bean
public FactoryBean(final Environment env) {
return new MyFactoryBean(env);
}
}
The Environment instance will give you access to all the properties, because it is a PropertyResolver You can programmatically loop over the properties
int x = 1;
while(true) {
env.getRequiredProperty("requests." + x + ".A")
env.getRequiredProperty("requests." + x + ".B")
}
If you want to use that to create instances of a specific bean I would suggest using a PropertiesBeanDefinitionReader. Specify 2 property files to load (one well known to configure the defaults) and one to add beans.
core-request.properties
request.(class)=com.company.pkg.Request
requests.properties
requests1.A=1
requests1.B=2
requests2.A=3
requests2.B=4
requests3.A=5
requests3.B=6
Now you can use your FactoryBean to construct a BeanFactory and obtain all the beans and expose them as a list.
public class YourFactoryBean implements FactoryBean<List<Request>> {
#Autowired
private ResourceLoader rl;
public Object getObject() {
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory;
PropertiesBeanDefinitionLoader loader = new PropertiesBeanDefinitionLoader(beanRegistry);
Resource core = rl.getResource(core-location);
Resource requests = rl.getResource(requests-location);
loader.loadBeanDefinitions(core);
loader.setDefaultParentBean("request");
loader.loadBeanDefinitions(requests);
return beanRegistry.getBeansOfType(Request.class).values();
}
}
Something like that.

How do you make dynamic bindings in Guice that require an injected Instance?

I'd like to create a Module that dynamically binds instances to named annotations. The use case is I would like to automatically bind the values in my configuration with the key in the properties file being the #Named value.
However the configuration is bound in a different module so I need the config to be injected. Solutions I've looked at are:
Binding in the configure() method.
This method is not injected into and I can not get the base configuration.
Using a Provider/#Provides.
Providers only bind a single instance.
Using MultiBinder.
My use case is a little different then what is provided by this extension. Multi-binding allows you to bind multiple instances separately and then have them injected as a Collection more complex containing type. I would like to bind each instance separately and have them by uniquely identifiable for injection latter.
Use a childInjector.
Unfortunately this isn't possible without some extensive modification of existing code. This answer is a very good description of how to solve this problem this way though.
Inject the binder somehow. (I started getting a little hackier)
Guice allows injecting the Injector for latter use, I tried injecting the Binder into the Module though a #Provides method and then using the binder directly to make multiple binds within the method. Guice would not inject the binder.
Remember that all of the configure methods configure all of the bindings in an Injector before any injection can happen. That said, a few things:
Binding #Named properties to the contents of a single Properties instance is so useful, there's a Names.bindProperties(...) method that does it automatically for you. The only trick is that you need to have the Properties instance at the time configure() is run.
If they're all available at the same time, don't worry about binding the properties in one module and binding the application in another. As long as they all go into the same Injector, Guice will combine them all and let them satisfy each others' dependencies.
Providers can return different instances, and usually do--but you're right that it won't help you differentiate between keys. If injecting the Properties instance directly is too ugly, consider making a lightweight factory instead:
public class ConfigOracle {
#Inject private Properties properties;
public String getAsString(String key) { ... }
public int getAsInt(String key) { ... }
}
public class SomeConfigUser {
#Inject private ConfigOracle configOracle;
public void doStuff() {
doStuffBasedOn(configOracle.getAsString("my.properties.key"));
}
}
You should never need to inject a Binder (or anything else) into a Module.
If you implement Module, the binder will be a parameter of configure(). If you extend AbstractModule as you should, just call the binder() method.
You can pass in dependencies through constructor arguments to the Module, if need be, which (as far as I'm concerned) is the only way Modules should vary the bindings they create.
There's no reason you couldn't create a Module through an Injector, but you'd have to have an Injector first, and it sounds like you're trying to get away with only having one.
If you need other instances from the Injector you can always write a Provider implementation with #Inject fields/methods/constructors, or even take in parameters in a #Provides method (which will be filled in with dependencies automatically).
Overall I still favor the child injector approach (thanks for the link and compliment to my previous answer!), which fits your "dynamic bindings based on an injected instance" description the best, and would literally be this simple:
class PropertiesModule extends AbstractModule {
Properties properties;
PropertiesModule(Properties properties) {
this.properties = properties;
}
#Override public void configure() {
Names.bindProperties(binder(), properties);
}
}
Injector oldInjector = Guice.createInjector(allYourOtherModules);
Module myModule = new PropertiesModule(oldInjector.get(Properties.class));
Injector injector = oldInjector.createChildInjector(myModule);

Categories