Situation
I have a fat .jar of a Spring boot application. I've externalized my configuration with an application.properties file. This file is in the same folder as the .jar, and I'm starting the .jar from the command line from within the same folder (with the command "java -jar $jarFileName").
Then an exception is thrown:
nested exception is org.springframework.beans.TypeMismatchException:
Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is
java.lang.NumberFormatException: For input string: "${elasticsearch.port}"
As you can see, instead of reading the value from the properties file, it just sets the string as the text in the #Value annotation, which looks like this:
#Value("${elasticsearch.port}")
private int elkPort;
The class this happens in is annotated with #Component.
According to Spring docs: externalized configuration, spring should read an application.properties file outside of the jar.
When the same application.properties file is placed in src/main/resources it works fine, so the configuration file seems correct.
Any ideas why it won't load the external configuration file?
EDIT 1
I've also tried running it with --spring.config.location=file:application.properties and --spring.config.location=file:/full/path/to/application.properties but with the same result as above.
EDIT 2: classpath attempt
Also tried classpath instead of file, the same as the commands above but file replaced with classpath.
Lastly tried without either, so just --spring.config.location=/path/to/file; again both with relative and full path to the application.properties. All attempts gave the same result/exception.
EDIT 3
My annotated application:
#SpringBootApplication
public class ApplicationName {
public static void main(String[] args) {
SpringApplication.run(ApplicationName.class, args);
}
}
EDIT 4
Tried adding a PropertySourcesPlaceholderConfigurer as follows:
#Configuration
public class PropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
And then for each #Value I added a default value; it still only resolves to the default values instead of to the application.properties values.
Alright after quite some struggles, I've found the solution. I was close with PropertySourcesPlaceholderConfigurer but not quite there yet; this is the full class now:
#Configuration
public class PropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
final PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setIgnoreResourceNotFound(true);
final List<Resource> resources = new ArrayList<>();
resources.add(new FileSystemResource("relative/path/to/application.properties"));
ppc.setLocations(resources.toArray(new Resource[]{}));
return ppc;
}
}
EDIT
To demonstrate the issue, I've created a repository to show the problem, see here: https://github.com/Locitao/test-external-properties
As it says on mentioned page, you should specify external config location
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
Try without file keyword
--spring.config.location=/full/path/application.properties
I just took my application.properties out of an Eclipse Spring Boot project and it failed.
Then I put the file in a cfg folder in the root of the project and added program argument:
--spring.config.location=cfg/application.properties
and it worked again. Mayby if you try a relative path (no leading /) to the file (without the "file:") it will work.
Related
I want to set both property file (myproperty.properties) and log file location (myLogFile.log) through my own environment variable name (MYENV for example).
property file name must be different from spring boot application.properties name and log file has its own name also.
Do not want to use spring.config.name and spring.config.location.
MYENV will be set to "/locationFiles" value for example. myproperty.properties file location is "/locationFiles/config"
and myLogFile.log file location is "/locationFiles/log".
I know that I can use the following code snippet for reading my environment variable.
But How do I use propertiesLocation below to read the properties data in a simple Spring boot way?
I do not know how to define a corresponding java configuration class as It seems that configuration ppties file path cannot be set in a variable.
import org.springframework.core.env.Environment;
public class MyClass {
#Autowired
private Environment env;
String propertiesLocation;
private void propertyLocation() {
this.propertiesLocation = env.getProperty("MYENV")+"/config/";
}
}
The following code snippet does not match with what I want to do as I cannot
write something like that : #PropertySource(env.getProperty("MYENV")+"/config/")
#SpringBootApplication
#PropertySource("classpath:myproperty.properties")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I saw Environment Specific application.properties file in Spring Boot application but I does not match exactly with what I've described above.
As I want to define my own environment variable name and file names.
And I'm also looking for another way than using java -jar -Dspring.config.location=<path-to-file> myBootProject.jar as defined in Spring boot how to read properties file outside jar.
I want to know if there is an alternative way to this method.
I need to load a property from a .yml file, which contains the path to a folder where the application can read files from.
I'm using the following code to inject the property:
#Value("${files.upload.baseDir}")
private String pathToFileFolder;
The .yml file for development is located under src/main/resources/config/application.yml, im running the application with the following command in production, to override the development settings:
java -jar app.jar --spring.config.location=/path/to/application-production.yml
The Spring Boot documentation says:
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
As well as:
You can also use YAML ('.yml') files as an alternative to '.properties'.
The .yml file contains:
{...}
files:
upload:
baseDir: /Users/Thomas/Code/IdeaProjects/project1/files
{...}
And my Application class is annotated with:
#SpringBootApplication
#EnableCaching
When I run the application, i get an exception:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'files.upload.baseDir' in string value "${files.upload.baseDir}"
Do I have to use the YamlPropertySourceLoader class or add a special annotation to enable the support for .yml in Spring Boot?
Edit:
The .yml file contains some other properties, which get successfully loaded by Spring Boot like dataSource.XXXor hibernate.XXX.
For example: application.yml
key:
name: description here
Your Class:
#Value("${key.name}")
private String abc;
M. Deinum is right, the setup i've provided is working - the yml file was indented wrong, so the property couldn't be found.
I found the above wasn't working for me, because I tried to access the variable in a constructor. But at construction, the value is not injected yet.
Eventually I got it to work using this workaround: https://mrhaki.blogspot.com/2015/04/spring-sweets-using-value-for.html
Maybe this is helpful to others.
For me a duplicate key in the property file caused this...
I used same key unknowingly in large yml file.
key:
key1: value
key2: value
key:
key3: value
In yml properties file :
xxxx:
page:
rowSize: 1000
Create your Yaml properties config class :
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "xxxx")
public class YmlPropertiesConfig {
private Page page;
public Page getPage() {
return page;
}
public void setPage(Page page) {
this.page = page;
}
public class Page {
private Integer rowSize;
public Integer getRowSize() {
return rowSize;
}
public void setRowSize(Integer rowSize) {
this.rowSize = rowSize;
}
}
}
Finally get it and use it :
public class XXXXController {
#Autowired
private YmlPropertiesConfig ymlProperties;
public String getIt(){
Integer pageRowSize = ymlProperties.getPage().getRowSize();
}
}
I've got that issue Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder cause I've set test spring boot profile in properties.yaml.
Spring can't find properties for test profile when run app with no profile.
So remove spring boot profile from properties or yaml or run app with enabled profile.
Configuration file example is below:
#Configuration
public class AppConfig {
#Value("${prop.foo}")
private String foo;
#Value("${prop.bar}")
private String bar;
#Bean
BeanExample beanExample() {
return new BeanExample(foo, bar);
}
}
For those who have problems with a #RestController, I do it as follows:
#Autowired
#Value("${google.recaptcha}")
private String keyRecaptcha;
My properties file was mistakenly named applcation.properties as it was auto-generated by the Spring initializer. But I added the properties there in the .yml format and they were not retrieved with the same error.
When I renamed the file to application.yml, it started working.
I have multiple projects in which I am trying to read application.properties file from one particular project. There's one web application which I am deploying on tomcat. I want to use application.properties from another project.
I tried to import appconfig.java from that project to webAppconfig.java.
application.properties file in 1st project src/main/resources/application.properties contains driver=org.postgresql.Driver
1st project : Appconfig.java
#Configuration
#PropertySource("classpath:application.properties")
public class Appconfig{
#Value("${driver}")
private String test;
}
2nd project : WebAppconfig.java
#Import(Appconfig.java)
public class WebAppconfig{
}
I was expecting test=org.postgresql.Driver
but I am getting exception as key "driver" not found.
If I change application.properties to something.properties, it works.
I tried following ways:
a)
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
b) tried changing in build path as included, excluded as shown in image
c)I tried autowiring environment as well.but is didn't work..
I debugged environment variable ..under property sources.it shows{spring.application.name="project 2 name"}
So I have read dosens af articles on how to configure Spring boot to be aware of more yml files than application.yml and how to include these - even from subprojects. It is however hard to come by articles describing the same for "pure" Spring. I think however that i'm heading in the right direction I just can't get my configuration values back.
It's a straight forward multi-project gradle build with - for simplicity - two projects. One project is the "main" spring project - ie. Spring Context is initialized in this project. The other is a "support" module with some database entities and datasource configuration. We use annotation based configuration.
I would like to be able to define a set of configuration properties in the support module and based on whatever spring profile is active, the datasource configuration is loaded accordingly.
This SA post got me quite far following the different links in the different answers and composing my solution from this. The structure and code is as follows:
mainproject
src
main
groovy
Application.groovy
resourcers
application.yml
submodule
src
main
groovy
PropertiesConfiguration.groovy
DataSource.groovy
resources
datasource.yml
The PropertiesConfiguration.groovy adds the datasource.yml by using PropertySourcesPlaceholderConfigurer:
#Configuration
class PropertiesConfiguration {
#Bean
public PropertySourcesPlaceholderConfigurer configure() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer()
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean()
yamlPropertiesFactoryBean.setResources(new ClassPathResource("datasource.yml"))
configurer.setProperties(yamlPropertiesFactoryBean.getObject())
return configurer
}
}
The Datasource.groovy should then read values based on the spring profile using (code reduced for readability):
#Autowired
Environment env
datasource.username = env.getProperty("datasource.username")
The env.getProperty returns null. No matter what spring profile is active. I can access the configuration value using the #Value annotation, however then the active profile is not respected and it return a value even if it is not defined for that profile. My yml looks (something) like this:
---
spring:
profiles: development
datasource:
username: sa
password:
databaseUrl: jdbc:h2:mem:tests
databaseDriver: org.h2.Driver
I can from Application.groovy inspect my ApplicationContext using a debugger and confirm that my PropertySourcesPlaceholderConfigurer exist and the values are loaded. Inspecting applicationContext.environment.propertySources it is NOT there.
What am I missing?
Using a PropertySourcesPlaceholderConfigurer does not add properties to Environment. Using something like #PropertySource("classpath:something.properties") on the class level of your configuration class will add properties to Environment, but sadly this does not work with yaml-files.
So, you would have to manually add the properties read from the yaml file to your Environment. Here is one way to do this:
#Bean
public PropertySourcesPlaceholderConfigurer config(final ConfigurableEnvironment confenv) {
final PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource.yml"));
configurer.setProperties(yamlProperties.getObject());
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
return configurer;
}
With this code, you can inject properties in either of these two fashions:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = PropertiesConfiguration.class)
public class ConfigTest {
#Autowired
private Environment environment;
#Value("${datasource.username}")
private String username;
#Test
public void props() {
System.out.println(environment.getProperty("datasource.username"));
System.out.println(username);
}
}
With the properties supplied in the question, this will print "sa" two times.
Edit: It doesn't seem that the PropertySourcesPlaceholderConfigurer is actually needed now, so the code can be simplified to the below and still produce the same output.
#Autowired
public void config(final ConfigurableEnvironment confenv) {
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource.yml"));
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
}
Edit 2:
I see now that you are looking to use the yaml-file with multiple documents in one file, and Spring boot-style selection by profile. It does not seem to be possible using regular Spring. So I think you have to split your yaml files into several, named "datasource-{profile}.yml". Then, this should work (perhaps with some more advanced checking for multiple profiles, etc)
#Autowired
public void config(final ConfigurableEnvironment confenv) {
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource-" + confenv.getActiveProfiles()[0] + ".yml"));
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
}
Edit 3:
It could also be possible to use functionality from Spring boot without doing a full conversion of your project (I haven't actually tried it on a real project though). By adding a dependency to org.springframework.boot:spring-boot:1.5.9.RELEASE I was able to get it working with the single datasource.yml and multiple profiles, like this:
#Autowired
public void config (final ConfigurableEnvironment confenv) {
final YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();
try {
final PropertySource<?> datasource =
yamlPropertySourceLoader.load("datasource",
new ClassPathResource("datasource.yml"),
confenv.getActiveProfiles()[0]);
confenv.getPropertySources().addFirst(datasource);
} catch (final IOException e) {
throw new RuntimeException("Failed to load datasource properties", e);
}
}
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!