Autowire Spring Bean based on boolean variable - java

I want to configure the spring beans in such a way that depending on the value of a boolean variable, one of the two available connection bean gets autowired in the code.
Below is the initialization of the boolean variable:
//This is overridden as false from the properties file on the server.
#Value(value = "${my.property.connectionOne:true}")
private boolean connectionOne;
I have defined the Bean in such a way:
#Bean(name = "specificConnection")
public Destination getSpecificConnection() throws Exception {
if (connectionOne) { //boolean variable
return new ConnectionOne("DB");
}
else {
return new ConnectionTwo("XML");
}
}
where ConnectionOne and ConnectionTwo both implement Destination
And I am using the bean in the desired class as:
#Autowired
#Qualifier(value = "specificConnection")
private Destination specificConnection;
However, it doesn't seem to work. It keeps returning ConnectionOne only even if I change the value of the boolean variable to false.
I am using Spring version 4.2.0 and Wildfly Server.
Please let me know if any further clarification is required.

I want to configure the spring beans in such a way that depending on
the value of a boolean variable
The boolean variable has to be valued before the initialization of the specificConnection bean by Spring.
So what you should probably do is using a value expression.
#Value("${isConnectionOne}") // looks the value in the available placeholder
private boolean isConnectionOne;
#Bean(name = "specificConnection")
public Destination getSpecificConnection() throws Exception {
if (connectionOne) { //boolean variable
return new ConnectionOne("DB");
}
else {
return new ConnectionTwo("XML");
}
}

This is a perfect example for spring profiles! Have a look on this link:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
In Spring, you can define different profiles your program will run in. Based on settings you define in your application.properties your program will use different beans of these profiles. :)
I hope that could help you!
Greethings
Noixes

Related

Unable to resolve variable from properties file when tried to access as function parameter using #Value annotation

This may be silly question to ask but i'm unable to find any satisfactory solution to my problem. In java we don't have the concept of default variables so i am trying to give default value from properties file to my function parameters/arguments using #Value annotation, but i'm always getting null and i'm unable to figure why is this happening. Please help me to solve the issue or provide me some appropriate link/reference which may solve my issue.
MainApplication.java
#SpringBootApplication
public class Application
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(NetappApplication.class, args);
Sample sample = context.getBean(Sample.class);
System.out.println(sample.check(null));
}
}
Sample.java
public interface Sample
{
public String check(String message);
}
SampleImpl.java
#Service
#PropertySource("classpath:app.properties")
public class SampleImpl implements Sample
{
#Value("${test}")
String message1;
#Override
public String check(#Value("${test}") String message)
{
return message;
}
}
app.properties
test=anand
But you are passing null to your method...
Perhaps what you want to do is to assign default value to test in case it's not defined in property file:
#Value("${test:default}");
Then, when properties are autowired by Spring if placeholder resolver doesn't get the value from props file, it will use what is after :.
The best use case for this (that I can think of) is when you create Spring configuration.
Let's say you have a configuration class: for DB access. Simply put:
#Configuration
public class DbConfig {
#Value("${url:localhost}")
String dbUrl;
// rest for driver, user, pass etc
public DataSource createDatasource() {
// here you use some DataSourceBuilder to configure connection
}
}
Now, when Spring application starts up, properties' values are resolved, and as I wrote above you can switch between value from property and a default value. But it is done once, when app starts and Spring creates your beans.
If you want to check incoming argument on runtime, simple null check will be enough.
#Value("${test}")
String message1;
#Override
public String check(String message) {
if (message == null) {
return message1;
}
}

Dynamic injection of bean properties spring

So my problem is, I have a bean which I would like to inject dynamically based on runtime values. Lets say I have class A which has private variable B. I would like to inject my variable B (upon creation of the bean) via spring with value I got from user (for example). How can I do that ? Should I just use getBean() and then use setter method for my variable or is there any better way ?
EDIT:
#Bean
class A {
private int B;
...
}
main {
context = someContext("myConfigFileWhereBeansAreDefined");
int value = getIntFromUser();
// I want to have myNewBean injected with "value" i got from user
A myNewBean = context.getBean("A");
}

Spring - How to get subset of properties file without enumerating all required properties

I am loading my properties file as following:
#Configuration
#PropertySource("classpath:app.properties")
class MyApp {
#Bean
public PropertySourcesPlaceholderConfigurer PropertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In the properties file, I have several database-related properties:
database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb
database.dataSource.x=...
database.dataSource.y=...
database.dataSource.z=...
Note:
${db-port} should be replaced by either the value of property/environment variable db-port or 5432. In my case, I am defining the environment variable db-port when spawning the Tomcat container.
All database-related properties are grouped under database. root. This is intentional, see below.
I want to avoid that I have to enumerate/hardcode all possible database-related properties in my code. Luckily, the database layer in use (Hikari) has the nice feature that I can pass all properties via a java.util.Properties. So, I want retrieve all defined properties under database.* and simply forward it to Hikari.
For this, I wrote the following utility:
#Component
public class PropertyFetcher
{
#Autowired
private ConfigurableEnvironment environment;
public Properties get(final String key) {
final Properties p = new Properties();
for (final PropertySource<?> s : environment.getPropertySources()) {
if (s instanceof EnumerablePropertySource) {
for (final String k : ((EnumerablePropertySource) s).getPropertyNames()) {
if (k.startsWith(key) && k.length() > key.length()) {
p.put(k.substring(key.length()), s.getProperty(k));
}
}
}
}
return p;
}
}
Now, when calling get("database."), I have all database-related properties as defined in the properties file. Great! But, the value for property dataSource.url is now
jdbc:postgresql://localhost:${db-port:5432}/mydb
instead of
jdbc:postgresql://localhost:9876/mydb
So, for some reason, the ${db-port:5432} is not resolved (yet?) when going via this route (ConfigurableEnvironment).
How can this be fixed? Or is there a better way to get all properties under a certain root without having to enumerate/hardcode them into the code?
Please note that in the default scenario, the ${db-port:5432} in property database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb is correctly resolved. I tested this by defining the following member and logging it:
#Value("${database.dataSource.url}")
final String url; // holds jdbc:postgresql://localhost:9876/mydb
You should read the property values from real environment only. Then only you will get actual or effective value of a property.
This will require a little change in your code.
change this line:
p.put(k.substring(key.length()), s.getProperty(k));
to this:
p.put(k.substring(key.length()), environment.getProperty(k));

Can I access Spring-defined properties from within a Spring Condition?

Hopefully my question is fairly self-explanatory - I will illustrate it with some example code.
#Component
#PropertySource("classpath:/my-properties.properties")
public class SomeProperties {
#Autowired
Environment env;
// private methods
public boolean isEnabled(Foo foo) {
// call private methods, call env.getProperty, return value
}
}
#Component // it makes no difference whether it's there or not
public class MyCondition implements Condition {
#Autowired // doesn't make a difference
private SomeProperties someProperties;
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// ...
boolean b = someProperties.isEnabled(foo); // get NPE on this line
// ...return
}
}
#Component
#Conditional(MyCondition.class)
public class Bar {
// stuff
}
(Here I'm using Spring Boot to configure Spring. Although I doubt it makes any difference - as #Component beans are definitely accessible post-bootstrap, so it doesn't seem to be a problem in the way Spring is configured.)
The problem is that I'm getting a NullPointerException on the indicated line, because someProperties is null. This is presumably because at the time that the condition is run, the autowiring/instantiation phase of Spring bootstrap has not happened yet.
Is there any way to access Spring Properties in this way - like force Spring to load a bean before it normally would? Or is the only way to use standard Java / Apache Commons properties code as opposed to Spring?

Spring - Replacing the bean property values with new Property File values

I have a property file and using Spring property place holder, I set values to the Spring beans. Now, this property file may be modified during the run time. Is there a way to refresh the properties of the Spring beans with this newly modified property value? Especially, I have many singleton beans? How can I refresh them with the new values? Is there already a solution to this or should it be custom coded? If it doesn't already exist, can someone please give the best approach to achieve this? Thanks!
PS: My application is a batch application. I use Spring based Quartz configuration to schedule the batches.
I'll leave this in for reference, but the updated answer is below the divider:
Well the ConfigurableApplicationContext interface contains a refresh() method, which should be what you want, but the question is: how to access that method. Whichever way you do it, you'll start with a bean that has a dependency of type ConfigurableApplicationContext:
private ConfigurableApplicationContext context;
#Autowired
public void setContext(ConfigurableApplicationContext ctx){
this.context = ctx;
}
Now the two basic options I'd suggest would be to either
use the Task Execution Framework and let your bean watch the property resources regularly, refreshing the ApplicationContext when it finds changes or
expose the bean to JMX, allowing you to manually trigger the refresh.
Referring to comments: since it seems impossible to refresh the entire context, an alternative strategy would be to create a properties factory bean and inject that into all other beans.
public class PropertiesFactoryBean implements FactoryBean<Properties>{
public void setPropertiesResource(Resource propertiesResource){
this.propertiesResource = propertiesResource;
}
private Properties value=null;
long lastChange = -1L;
private Resource propertiesResource;
#Override
public Properties getObject() throws Exception{
synchronized(this){
long resourceModification = propertiesResource.lastModified();
if(resourceModification != lastChange){
Properties newProps = new Properties();
InputStream is = propertiesResource.getInputStream();
try{
newProps.load(is);
} catch(IOException e){
throw e;
} finally{
IOUtils.closeQuietly(is);
}
value=newProps;
lastChange= resourceModification;
}
}
// you might want to return a defensive copy here
return value;
}
#Override
public Class<?> getObjectType(){
return Properties.class;
}
#Override
public boolean isSingleton(){
return false;
}
}
You could inject this properties bean into all your other beans, however, you would have to be careful to always use prototype scope. This is particularly tricky inside singleton beans, a solution can be found here.
If you don't want to inject lookup methods all over the place, you could also inject a PropertyProvider bean like this:
public class PropertiesProvider implements ApplicationContextAware{
private String propertyBeanName;
private ApplicationContext applicationContext;
public void setPropertyBeanName(final String propertyBeanName){
this.propertyBeanName = propertyBeanName;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException{
this.applicationContext = applicationContext;
}
public String getProperty(final String propertyName){
return ((Properties) applicationContext.getBean(propertyBeanName)).getProperty(propertyName);
}
}

Categories