Spring Looking at Default Value Before Resolving Placeholder - java

I've been searching forever for an issue similar to mine, but wasn't able to find one. Thus, I hope it's not a duplicate post.
Well,
I'm using spring integration to search for documents in my mongodb. Once it finds one, it sends the payload to another method.
I have a property file that I want to be resolved on this find and send configuration, so I use placeholders instead of static values.
Here's my xml:
<int:inbound-channel-adapter id="sendClientMailInboundAdapter"
expression="#repository.findClient()"
channel="sendClientMailChannel"
auto-startup="${send.mail.active:false}">
<int:poller fixed-rate="${send.mail.poller.time:60}" time-unit="SECONDS" max-messages-per-poll="1" />
</int:inbound-channel-adapter>
<int:channel id="sendClientMailChannel" />
<int:service-activator input-channel="sendClientMailChannel" expression="#service.sendClientMail(payload)" />
Right.
Now.. I've got an AppConfig class which loads the property file.
#Configuration
#PropertySource("file://${appconfig.root}/appconfig.properties")
public class AppConfig {
public static Environment env;
...
#Bean
public static PropertySourcesPlaceholderConfigurer appConfigConfigurer(Environment env) {
AppConfig.env = env;
PropertySourcesPlaceholderConfigurer appConfigConfigurer = new PropertySourcesPlaceholderConfigurer();
appConfigConfigurer.setIgnoreUnresolvablePlaceholders(true);
appConfigConfigurer.setIgnoreResourceNotFound(true);
return appConfigConfigurer;
}
}
So.. My problem is the default value.
If I do NOT specify the default value, spring resolves the placeholder. If it's specified, though, it seems spring ignores the placeholder (maybe it doesn't wait for it to resolve) and set the default value instead!
I could use context:property-placeholder location. I've done that and it worked, but since I'm already loading the file on my configuration class, I'd rather have spring read properties from there so I would not have to remember to adjust two files in case property file changes its folder, for instance.
My question: Is there a way to tell spring to wait for the placeholder to resolve before looking at the default value?
edit: Just so people know.. I changed xml to java config, using placeholder / default value like before and it worked perfectly.
Here is the equivalent snippet:
#InboundChannelAdapter(value = "sendClientMailChannel", autoStartup="${send.mail.active:false}",
poller = #Poller(fixedRate="${send.mail.poller.time.in.millis:60000}", maxMessagesPerPoll="1"))
public Client findClient() {
return repository.findClient();
}
#ServiceActivator(inputChannel="sendClientMailChannel")
public void sendToClient(Client payload) {
service.sendClientMail(payload);
}
#Bean
public DirectChannel sendClientMailChannel() {
return new DirectChannel();
}
note: I didn't have to ref the property file.

Related

Spring #Value only uses default value

I am injecting a value to a variable with #Value. For some reason, when I have the default value, it only uses it, it doesn't look for it in the properties file. When I'm not using the default value, it does inject the value from the properties file. No other configurations were changed.
#Value("${migration.paths:#{'classpath:db/migration'}}")
private String dbMigrationPaths;
(I'm using SPEL in the default value because it has slashes)
The property file configuration:
#Bean
public static PropertySourcesPlaceholderConfigurer configDataSourcesPropertyFile() {
PropertySourcesPlaceholderConfigurer bean = new PropertySourcesPlaceholderConfigurer();
bean.setLocations(new ClassPathResource[]{
new ClassPathResource("/file1"),
new ClassPathResource("/file2")
});
bean.setIgnoreUnresolvablePlaceholders(true);
bean.setIgnoreResourceNotFound(true);
return bean;
}
Both are properties files, and the property in question resides in file1 and not in file2
Do you have two property placeholders in your project? If yes, you may be running into this bug documented here: https://jira.spring.io/browse/SPR-9989. See at the end there is a link to suggested workaround.

Find message in mulitple properties files

In an application ther are multiple properties file for managing exception messages , alerts , and some others text these file like this :
- core-message.properties
- databaseException.properties
......
in Service layer maybe a database call occure and the database return a key that exist in one the properties files , and i want get the value and raise the exception messsage to user interface layer .
if i know that the key in wich properties file the code will be like this :
#Value("#{core['theExceptionKey']}")
public String excpetionMessage;
private void myMethod() {
throw new ExceptionClass(exceptionMessage);
}
i think spring can do that because when i use spring:message tag in jsp files spring does not know the key in witch file but it load the message correctly.
You can use Spring Environment abstraction for that.
First you need to add Property Source to your Java Configuration file
#Configuration
#PropertySource("classpath:/com/mypacakge/core-message.properties")
public class AppConfig {
Or if you have multiple properties files
#Configuration
#PropertySources({
#PropertySource("classpath:core-message.properties"),
#PropertySource("classpath:database.properties")
})
public class AppConfig {
Add PropertySourceConfigurer to to your Java Configuration file
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Now let's say that in your core-message.properties you have the following data
message.name=Hello
You can retrieve this data in any bean by autowiring Environment abstraction and then calling env.getProperty()
#Autowired
Environment env;
public void m1(){
String message = env.getProperty("message.name")` // will return Hello
Environment object provides interface to configure property sources and resolve properties. It provides convenience to read from a variety of sources: properties files, system environment variable, JVM system properties, servlet context parameters, and so on, which is very useful. For example :
environment.getSystemProperties().put("message", "Hello");
System.getProperties().put("message", "Hello");
environment.getSystemProperties().get("message"); // retrieve property
environment.getPropertySources() // allows manipulation of Properties objects
Spring Reference Documentation - Environment
To get the value of the key programmatically you can use the following:
#Autowired
private Environment env;
...
String something = env.getProperty("property.key.something");

How to read properties (or any other text) file in Java Spring MVC app?

I need to read java properties file inside my Spring MVC app but I can't find the way to do that. I tried several answers from similar question here on SO, but I was unsuccessful. I'm new to Java, and especially Spring MVC so I probably messed up something.
I'm not sure anymore that the file is being successfully deployed. I'm using Tomcat btw.
If you are using Spring 3.1+ you can use the #PropertySource annotation:
#Configuration
#PropertySource("classpath:/com/example/app.properties")
public class AppConfig {
// create beans
}
or for XML-based configuration you can use the <context:property-placeholder>:
<beans>
<context:property-placeholder location="classpath:com/example/app.properties"/>
<!-- bean declarations -->
</beans>
then you can autowire the key in the properties file using the #Value annotation:
#Value("${property.key}") String propertyValue;
Read more details in the Spring reference docs.
You can have properties files automatically loaded in Spring by using the PropertySourcesPlaceholderConfigurer.
Here is an example of configuring a PropertySourcesPlaceholderConfigurer using Spring JavaConfig:
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer props = new PropertySourcesPlaceholderConfigurer();
props.setLocations(new Resource[] {
new ClassPathResource("/config/myconfig.properties"),
new ClassPathResource("version.properties")
});
}
This will load the properties from the files above on the classpath.
You can use these properties in property replacements within your application. For example, assume that there is a property in one of those files above named myprop. You could inject myprop's value into a field using the following:
#Value(${myprop})
private String someProperty;
You can also access the values of the properties by injecting Spring's Environment object into your classes.
#Resource
private Environment environment;
public void doSomething() {
String myPropValue = environment.getProperty("myprop");
}
In order to read any old file from within a web application the link that Frederic posted in the comments above provides a good explanation of the normal classloader hurdles one encounters when attempting to read files from within their war file and the solutions around it.
You can try the below code.
Add this to servelt-context.xml
<context:property-placeholder location="classpath:config.properties"/>
And to access the contents of config file in java,
#Value("${KEY}")
private String value;

Spring configuration

Let's say we have a bean definition in spring configuration
<bean id="scanningIMAPClient" class="com.acme.email.incoming.ScanningIMAPClient" />
What I really want is the scanningIMAPClient to be of type com.acme.email.incoming.GenericIMAPClient if the configured email server is a normal IMAP server and com.acme.email.incoming.GmailIMAPClient incase it is a GMAIL server, (since gmail behaves in slightly different way) GmailIMAPClient is a subclass of GenericIMAPClient.
How can I accomplish that in spring configuration?
There is a properties file which contains configuration of the email server.
It's simple with Java configuration:
#Value("${serverAddress}")
private String serverAddress;
#Bean
public GenericIMAPClient scanningIMAPClient() {
if(serverAddress.equals("gmail.com"))
return new GmailIMAPClient();
else
return new GenericIMAPClient();
}
You can emulate this behaviour with custom FactoryBean.
You can use programatic configuration:
#Configuration
public class AppConfig {
#Bean(name="scanningIMAPClient")
public GenericIMAPClient helloWorld() {
...check config and return desired type
}
}
More info here.

Spring #Value annotation not using defaults when property is not present

I am trying to use #Value annotation in the parameters of a constructor as follows:
#Autowired
public StringEncryptor(
#Value("${encryptor.password:\"\"}") String password,
#Value("${encryptor.algorithm:\"PBEWithMD5AndTripleDES\"}") String algorithm,
#Value("${encryptor.poolSize:10}") Integer poolSize,
#Value("${encryptor.salt:\"\"}") String salt) {
...
}
When the properties file is present on the classpath, the properties are loaded perfectly and the test executes fine. However when I remove the properties file from the classpath, I would have expected that the default values would have been used, for example poolSize would be set to 10 or algorithm to PBEWithMD5AndTripleDES however this is not the case.
Running the code through a debugger (which would only work after changing #Value("${encryptor.poolSize:10}") Integer poolSize to #Value("${encryptor.poolSize:10}") String poolSize as it was causing NumberFormatExceptions) I find that the defaults are not being set and the parameters are in the form of:
poolSize = ${encryptor.poolSize:10} or
algorithm = ${encryptor.algorithm:"PBEWithMD5AndTripleDES"}
rather than the expected
poolSize = 10 or
algorithm = "PBEWithMD5AndTripleDES"
Based on SPR-4785 the notation such as ${my.property:myDefaultValue} should work. Yet it's not happening for me!
Thank you
Perhaps initialization of property placeholder configurer fails due to missed properties file, so that placeholders are not resolved. You can configure it to ignore missed files as follows (if you use context namespace to configure it):
<context:property-placeholder ignore-resource-not-found="true" ... />
Also you don't need "..." around default values.
ignore-resource-not-found="true" is not necessary for the defaults to be picked up. The point of specifying the default value is for it to be used if the property is not found anywhere.
I think the last sentence in the previous answer points to the problem - incorrect EL that you must have originally provided but then removed from the example. The fact that you were getting format conversion exceptions points to that as well. Normally, Spring will automatically convert Strings to the appropriate "standard" Java type, and if you provide your own implementation of the Spring Conversion Service, to your custom objects as well - as long as your conversion service is defined in the app context.
"ignore-resource-not-found" is useful when you are injecting properties via XML without defaults and don't want the container to throw an exception instantiating the bean in case no property is found. In such cases the bean properties will be initialized with the Java defaults, e.g. nulls fro objects, 0s for primitive numeric values, etc.
In my case, resolving the property values (and the defaults) did not work in test, where I use an annotation based configuration. It turned out that I had to add a PropertySourcesPlaceholderConfigurer so that properties actually get resolved. It was explained in the PropertySource Annotation JavaDoc:
In order to resolve ${...} placeholders in definitions or #Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using in XML, but must be explicitly registered using a static #Bean method when using #Configuration classes. See the "Working with externalized values" section of #Configuration Javadoc and "a note on BeanFactoryPostProcessor-returning #Bean methods" of #Bean Javadoc for details and examples.
The following did the trick:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
And if you want to add individual properties:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
Properties properties = new Properties();
properties.put("batchSize", "250");
propertySourcesPlaceholderConfigurer.setProperties(properties);
return propertySourcesPlaceholderConfigurer;
}

Categories

Resources