Property from file is not found with Spring - java

I am running into a problem where I can't access a Spring property from my Java code.
Here is the context:
<context:property-placeholder ignore-resource-not-found="false"
location="file:/${setup.properties.directory}/setup.properties"/>
The setup.properties file looks like this:
paymentProvider.x.url=x
The code is:
SpringContext.INSTANCE.getEnvironment()
.getProperty("paymentProvider.x.url");
There are no errors during run. However, the result of the code above gives null.
Anyone knows why?

Modify context:property-placeholder configuration like:
<context:property-placeholder location="classpath:setup.properties" />
Where you want to access this properties value on that class level use one annotation like:
#PropertySource(value = { "classpath:setup.properties" })
And to read the properties values autowire Environment inside that class like:
#Autowired
private Environment environment;
And finally you can access like:
environment.getRequiredProperty("paymentProvider.x.url");

Only
#Configuration
#PropertySource("file:/${setup.properties.directory}/setup.properties")
public class SpringContext {
public static final ApplicationContext PROPERTIES_INSTANCE = new AnnotationConfigApplicationContext
(SpringContext.class);
}
works for me.

Related

#refreshScope and #PropertySource how to?

Anybody know if #RefreshScope, applied to class for reloading properties/yml files dinamically works with a configuration class annotated only with #PropertySource?
I have to refresh an external configuration file, but i cant do something like :
#Bean
#RefreshScope
public FileProperties refreshListConfig() {
return new FileProperties(); //why ?
}
#Configuration //Configuration or new instance as above?
#PropertySource("file:${path.properties}")
#ConfigurationProperties(prefix="multitenancy")
public class FileProperties {
private List<DirProps> dir =new ArrayList<DirProps>();
private String tenantsFilePath;
..
class DirProps { ..}
...
}
I know that #RefreshScope doesn't work with #Configuration, but can I use #PropertySource without #Configuration?
Javadoc :
Annotation providing a convenient and declarative mechanism for adding a PropertySource to Spring's Environment. To be used in conjunction with #Configuration classes.
So, can't i use #RefreshScope without move external properties in application properties and removing #PropertySource and #Configuration annotations from FileProperties class? Do you know if exists a working approach without move the properties?
Thanks

How to load a properties file based on the server environment with spring so that the values can be injected?

To my surprise I have had a difficult time finding an answer to this question. I have Seen many examples where you can use #PropertySource to load a specific properties file for a class. I have also seen examples where you can easily add different property files in spring boot projects. But what I want to do is to do this for a spring project that is NOT spring boot and load a properties file so that the values of this file can be injected in classes annotated with #Component which is dependent on the server environment. So for example if I am on development server I want a particular properties file loaded and on production a different properties file. The reason that I am doing it like this is because my data and service layers are their own modules. These modules contain their own unit tests and can be imported as their own modules in other spring boot projects. I need properties files to be loaded to serve these modules which use spring but not spring boot. I have tried the following, but this does not work.
#Configuration
#Profile("test")
#EnableJpaRepositories("com.hi.repository")
#EnableTransactionManagement
#EnableScheduling
public class InfrastructureConfig {
...
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
Map<String, String> env = System.getenv();
String propertiesFile=null;
String e = env.get("SERVER_ENV");
if (e.equals("dev")) {
propertiesFile = "environment/development.properties";
} else if (e.equals("prod")) {
propertiesFile = "environment/production.properties";
}
configurer.setLocation(new ClassPathResource(propertiesFile));
return configurer;
}
Then I have a test which looks like this
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/spring/DealServiceTest-context.xml"})
#ActiveProfiles("test")
public class LogTest {
private static final Logger log = LogManager.getLogger(LogTest.class);
#Autowired
PathsService pathsService;
#Autowired
Environment environment;
#Test
public void testBeans(){
System.out.println("********** WASSUP from LogTest");
System.out.println(environment.getProperty("imageBucket"));
}
Although the test prints out null which indicates to me the properties file has not been loaded and prepared for its values to be injected. How can I achieve this?
You don't really need to set properties yourself, but you can do this using spring configuration. Check the documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
If you're using spring boot - all you need to do is create multiple properties file for your environments. And only for properties you need to override.
So your main properties file would be at
src/main/resources/application.properties
Production
src/main/resources/application-prod.properties
Development
src/main/resources/application-dev.properties
Testing
src/main/resources/application-test.properties
And then just use the profile name as your environment variable
java -jar -Dspring.profiles.active=prod demo-0.0.1-SNAPSHOT.jar
Actually, you can just use a placeholder in #PropertySource annotation.
See documentation:
Any ${...} placeholders present in a #PropertySource resource location will be resolved against the set of property sources already registered against the environment.
Assuming that placeholder is present in one of the property sources already registered, e.g. system properties or environment variables, the placeholder will be resolved to the corresponding value.
I've made a simple example, it receives a 'property.environment' value to choose, which .properties file should be used as property source. I have two resource files in my classpath - application-test.properties and application-dev.properties, each one contains a 'test.property' value ('test-env' and 'dev-env' respectively).
Property configuration:
#Configuration
#PropertySource("classpath:/config/application-${property.environment}.properties")
public class PropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
return propertySourcesPlaceholderConfigurer;
}
}
Component with #Value
#Component
public class TestService {
#Value("${test.property}")
String testProperty;
#PostConstruct
void init() {
System.out.println("---------------------------------------------------------");
System.out.println("Running in " + testProperty + " environment");
System.out.println("---------------------------------------------------------");
}
}
Build command line example (it runs tests with test environment properties)
mvn clean install -DargLine="-Dproperty.environment=test"
Output
---------------------------------------------------------
Running in test-env environment
---------------------------------------------------------
Run command line example
java -jar -Dproperty.environment=dev PATH_TO_YOUR_JAR.jar
Output
---------------------------------------------------------
Running in dev-env environment
---------------------------------------------------------
Don't hard code based on different environment, in spring boot you can able to maintain properties specific environment easily. Refer https://spapas.github.io/2016/03/31/spring-boot-settings/
I would try to take advantage of the profile mechanism already in place in Spring. You basically have done the job yourself already, the only thing you need to change is to have different configurations for "test" and "production" profiles. I prefer to keep everything related to test away from production code (allowing me to place the TestConfig class below in the test source path), so I would probably do something like this:
#Configuration
#Profile("!test")
#PropertySource(value = "classpath:/environment/production.properties")
#Import(AppConfig.class)
public class ProductionConfig
{
// Your production-specific config goes here
}
#Configuration
#Profile("test")
#PropertySource(value = "classpath:/environment/development.properties")
#Import(AppConfig.class)
public class TestConfig
{
// Your test-specific config goes here
}
#Configuration
public class AppConfig
{
// Needed for spring to handle ${property:default} syntax
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If you prefer to have one config for both cases, you can let the AppConfig import the TestConfig and the ProductionConfig instead, but that will put test code in to production...
Good luck with your project!

Spring and #Value annotation

I'm trying to build a console application with Spring framework.
I have a class annotated by #SpringBootApplication:
#SpringBootApplication
public class LoaderApplication {
public static void main(String[] args) {
SpringApplication.run(LoaderApplication.class, args);
}
}
And class annotated by #Component
#Component
public class ElasticLoader implements CommandLineRunner {
// Elastic properties
#Value("${elasticTransportAddress:#{null}}")
private String elasticTransportAddress;
private static final Logger log = LoggerFactory.getLogger(ElasticLoader.class);
#Override
public void run(String... arg) throws Exception {
if (!( elasticTransportAddress != null && elasticTransportAddress.isEmpty() )) {
log.error("[elasticTransportAddress] is not defined in property file");
return;
}
log.info("-> ElasticLoader.run");
}
}
As you can see, at this class I’m trying inject property elasticTransportAddress value by #Value annotation, but after running my application I can see that property elasticTransportAddress stays unassigned.
What did I miss?
Let me to add some notes:
I want to use my application with different property files. For this case I've created xml-context-config.xml with such content:
<beans profile="companies">
<!-- allows for ${} replacement in the spring xml configuration from the
application-default.properties, application-dev files on the classpath -->
<context:property-placeholder
location="classpath:companies.properties"
ignore-unresolvable="true" />
<!-- scans for annotated classes in the com.env.dev package -->
<context:component-scan base-package="ru.alexeyzhulin" />
</beans>
and use configuration file companies.properties which I placed in
My run configuration with the described profile:
And this profile is enabled, as I could see in the application logs:
2017-01-15 00:10:39.697 INFO 10120 --- [ main] ru.alexeyzhulin.LoaderApplication : The following profiles are active: companies
But when I define properties in application.properties and use default profile, the property elasticTransportAddress becomes assigned.
rename companies.properties to application-companies.properties. Link to documentation
You have an error in the if condition.
This:
if (!( elasticTransportAddress != null && elasticTransportAddress.isEmpty() ))
is equivalent to this:
if (elasticTransportAddress == null || !elasticTransportAddress.isEmpty())
Thus, you get the error log message + return in TWO cases:
If the property is not defined OR
If the property is defined and it's value is non-empty.
The fix is to rewrite the if condition, e.g.
if (elasticTransportAddress == null || elasticTransportAddress.isEmpty())
Update 1: You of course need to make sure this value is really defined in application properties.
Update 2: Your xml-context-config.xml file isn't used by Spring at all.
The recommended way is to use annotation-based configuration with #SpringBootApplication. You can do this by deleting xml-context-config.xml completely and creating a class annotated with #Configuration. This configuration-class can include annotations which define property sources. E.g.:
#Configuration
#Profile("companies")
#PropertySource("classpath:companies.properties")
public final class LoaderApplication {
}
Note that you don't need to explicitly enable #ComponentScan if you use #SpringBootApplication.
As the other #Alex said, you have to tell Spring from where you want to retrieve those properties. What you are missing here is another configuration and in your case (Annotation based configuration) you have to add this class:
#Configuration
public class MyConfiguration {
#Bean
public PropertyPlaceholderConfigurer getCompanyConfigurer() throws IOException {
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setLocations(new ClassPathResource("classpath:companies.properties"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}
}
Update:
If you are using the XML based configuration you have to tell Spring Boot what is the XML configuration deleting your annotation #SpringBootApplication in LoaderApplication and adding the XML to SpringApplication in this way:
SpringApplication.run("classpath:application-configuration.x‌​ml", args);
Finally add this line to the xml:
<context:property-placeholder ignore-unresolvable="true" location="classpath:companies.properties"/>
And, that's it, hope it helped.

Where do I put my XML beans in a Spring Boot application?

I'm getting back into Spring (currently v4). It's all wonderful now with #SpringBootApplication and the other annotations but all the documentation seems to forget to mention how I define other beans in XML!
For example I'd like to create an "SFTP Session Factory" as defined at:
http://docs.spring.io/spring-integration/reference/html/sftp.html
There is a nice bit of XML to define the bean but where on earth do I put it and how do I link it in? Previously I did a:
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
to specify the file name and location but now that I'm trying to use:
ApplicationContext ctx = SpringApplication.run(Application.class);
Where do I put the XML file? Is there a magic spring name to call it?
As long as you're starting with a base #Configuration class to begin with, which it maybe sounds like you are with #SpringBootApplication, you can use the #ImportResource annotation to include an XML configuration file as well.
#SpringBootApplication
#ImportResource("classpath:spring-sftp-config.xml")
public class SpringConfiguration {
//
}
You also can translate the XML config to a Java config. In your case it would look like:
#Bean
public DefaultSftpSessionFactory sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory();
factory.setHost("localhost");
factory.setPrivateKey(new ClassPathResource("classpath:META-INF/keys/sftpTest"));
factory.setPrivateKeyPassphrase("springIntegration");
factory.setPort(22);
factory.setUser("kermit");
return factory;
}
You can put this method in the class with the #SpringBootApplication annotation.
Spring boot ideal concept is avoid xml file. but if you want to keep xml bean, you can just add #ImportResource("classPath:beanFileName.xml").
I would recommend remove the spring-sftp-config.xml file. And, convert this file to spring annotation based bean. So, whatever class has been created as bean. Just write #Service or #Component annotation before class name. for example:
XML based:
<bean ID="id name" class="com.example.Employee">
Annotation:
#Service or #Component
class Employee{
}
And, add #ComponentScan("Give the package name"). This is the best approach.

#autowired need to add something to xml

I'm new to Spring. I read something about #Autowired. In the document, it seems when I use #Autowired, I have to modify the XML file, such as applicationContext.xml.
However, I read the code, and I just saw #Autowired in Java code, but I didn't see XML file at all. And it works well. How to use #Autowired, do I still need xml, if needed, how to use it?
Spring application can be configured using java. In that case you dont need application.xml.
#Configuration
public class AppConfig {
#Autowired
Environment env;
#Bean
public MyBean myBean(){
return new MyBean()
}
}
Here #Configuration bean enables the #autowired. Following is another good example on using autowiring
http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/
Hope it helps

Categories

Resources