Spring load properties file programmatically - java

This is how I load application.properties if the file is on the classpath.
#ComponentScan
#Configuration
#PropertySource("classpath:application.properties")
public class MyApplication {
// Have some public methods
}
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApplication.class);
MyApplication app = context.getBean(MyApplication.class);
// execute methods in app
}
}
We are trying to deploy the application where the properties file is stored externally on a Google Cloud bucket (GCS). I can load the properties file from GCS and save it in memory. How do I pass the properties to the Application context and override the properties loaded from the classpath?
If it matters, it's a standalone app, not Spring Boot.

If you have it in memory then it should not be a problem. Get rid of #PropertySource("classpath:application.properties") annotation and register PropertySourcesPlaceholderConfigurer bean manually.
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation([any implementation of Resource interface - use most applicable out of available or implement it by yourself]);
return properties;
}

You can read external properties file following way.
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource(value = "${external.properties}", ignoreResourceNotFound = true)
})
When you run the jar file, pass external.properties filepath following way
java -Dexternal.properties=file:/path_to_properties my-service.jar

Related

External properties file in spring

I have a java,spring and not spring boot command line program with maven , which when i build a jar with all dependencies using maven-assembly-plugin , it includes application.properties, what i need to know is how to read the external application.properties and not the jar one.
I am reading the property file as:
#PropertySource(value = { "classpath:application.properties" })
If I print the classpath, the classpath does only include the jar and not the current directory.
Here is what you can do:
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource(value = "file:/etc/config/my-custom-config.properties", ignoreResourceNotFound = true)
})
This ignoreResourceNotFound is available since spring 4.3 and is pretty self-explanatory.
You can also opt for "programmatic" approach. In pure spring (not spring boot as you've mentioned in the question):
#Configuration
public class CommonConfig {
...
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setLocations(new FileSystemResource("/etc/config/my-custom-config.properties"),
new ClassPathResource("config/application.properties"),
return ppc;
}
...
}
FileSystemResource is for accessing resources available externally in the file system
ClassPathResource is for accessing resources in the classpath
We use dynamically generated configuration files that are injected with environment specific secrets. The combination to get everything aligned is as follows:
Code:
myFunction(#Value("${custom.configuration.name}") final String customConfigurationName) {
InputStream inputStream=new ClassPathResource(customconfigFileName).getInputStream()){}
...
}
Template - Add a hard required path as this confirms the file is on disk and loaded:
- name: SPRING_CONFIG_LOCATION
value: file:./config/schema-${ENV}.yml
...
- name: custom.configuration.name
value: file:config/schema-${ENV}.yml

How to load Application.properties file dynamically based on Profile in Spring MVC?

Hi I am learning Spring MVC and I want to know How to load application.properties file dynamically.
I am adding HibernateConfig.java file and AppConfig.java file. I want to load application properties file dynamically using profiles. For Example: dev, test, prod. I have tried to use dynamic name application-{profile}.properties and also tried profile annotation. but not able to understand how they are actually working. I have created a different application.properties files.
application-dev
application-test
application-prod
This property file contains my DB related data. but I don't know how to set profile and how to load PropertySource based on a profile.
I have set the active profile in my appConfig file. Please help me in understanding how to configure profile and application.properties using spring MVC Java-based configuration. I have searched and found many solutions for XML based configuration but I haven't found any proper answer for java based configuration.
HibernateConfig.java
#Configuration
#EnableTransactionManagement
#ComponentScan({"com.project.configuration"})
#PropertySource(value = {"classpath:application.properties"})
public class HibernateConfiguration {
#Autowired
private Environment env;
#Bean
public LocalSessionFactoryBean sessionFactory(){
return sessionFactory;
}
#Bean
public DataSource dataSource(){
/* loading DB */
return dataSource;
}
#Bean
public Properties hibernateProperties(){
}
}
AppConfig.java
#Override
public void onStartup(ServletContext servletContext) throws ServletException
{
super.onStartup(servletContext);
servletContext.setInitParameter("spring.profiles.active", "dev");
}
I think you cant set this parameter at that time, its already too late. You have to start the app with specified profile (or set it in bootstrap file) . You can pass it as an argument or place it in:
application.properities
Under key: spring.profiles.active
When you set this to 'dev' it will read main application.properities and then the profile one. More about how to set it:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

Spring boot - load file from outside classpath

I have to load file from outside of my classpath.
The location depends of env properties:
in dev properties I want to load file from resources folder
in prod properties I want to load file from path (/location/file)
What is the best way to do it?
A possible solution is to use configuration properties and the use of Resource. For example, define your properties like this:
#ConfigurationProperties(prefix = "app")
public class SomeProperties {
private Resource file;
// Getters + Setters
}
Then enable your configuration properties by using the #EnableConfigurationProperties annotation on any class, for example your main class:
#SpringBootApplication
#EnableConfigurationProperties(SomeProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
To configure the file location, you can use the following in development:
app.file=classpath:test.txt
And in the production environment you could use:
app.file=file:/usr/local/test.txt
And now you can just autowire the SomeProperties class within any other service. The Resource class has a getFile() method that allows you to retrieve the file, but in addition it contains several other useful methods as well.

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!

Loading properties in Spring 3.1 programmatically

I am trying to create a AnnotationConfigApplicationContext programmatically. I am getting a list of Configuration classes and a list of property files to go with it in a Spring XML file.
Using that file, I am able to use XmlBeanDefinitionReader and load all #Configuration definitions fine. But, I am not able to load properties.
This is what I am doing to load properties..
PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
for (String propFile : propertyFiles) {
propReader.loadBeanDefinitions(new ClassPathResource(propFile));
}
Code just runs through this without any issues, but once I call
ctx.refresh() -- it throws an exception
Caused by: java.lang.IllegalStateException: No bean class specified on bean definition
at org.springframework.beans.factory.support.AbstractBeanDefinition.getBeanClass(AbstractBeanDefinition.java:381)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:54)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990)
All the classes are available on the classpath, if I just don't load the above properties programmatically application just comes up fine (Because I am using other way to load properties).
Not sure, what I am doing wrong here. Any ideas? Thanks.
I am not sure why you are loading properties manually, but Spring standard for AnnotationConfigApplicationContext is
#Configuration
#PropertySource({"/props1.properties", "/props2.properties"})
public class Test {
...
as for programatic loading, use PropertySourcesPlaceholderConfigurer instead of PropertiesBeanDefinitionReader, this example works OK
#Configuration
public class Test {
#Value("${prop1}") //props1.properties contains prop1=val1
String prop1;
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Test.class);
PropertySourcesPlaceholderConfigurer pph = new PropertySourcesPlaceholderConfigurer();
pph.setLocation(new ClassPathResource("/props1.properties"));
ctx.addBeanFactoryPostProcessor(pph);
ctx.refresh();
Test test = ctx.getBean(Test.class);
System.out.println(test.prop1);
}
}
prints
val1

Categories

Resources