I used to complete sample from Spring Data...
It works well.
I added application.properties
#spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.url=jdbc:h2:file:./h2/demo
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=wrong
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
I worked (apparently on first run it creates the database with whatever password defined).
When I changed the password to newWrong it started failing as expected (so I verified it's checking password).
Now I changed the property file to contain
spring.datasource.password=#{systemProperties['pass']}
and I changed the AccessingDataJpaApplication's main to:
public static void main(String[] args) {
System.setProperty("pass", "wrong" );
SpringApplication.run(AccessingDataJpaApplication.class);
}
and it is not working - still complaining about the password.
On the other hand, when I added
#Value("${bar}")
String foo;
and defined in application properties (and used wrong for password to prevent failing)
bar=#{systemProperties['pass']}
this statement in Application class
System.out.println("foo: " + foo);
prints foo: wrong.
Why the same is not working for spring.datasource.password property?
This might not be the exact answer to your question, but I believe it is the answer to what you are trying to solve.
You can simply pass the password to the app when you launch it on the command line by appending a -Dspring.datasource.password=wrong to the command. You can do that with any spring property.
If you are running from and IDE, you can edit the run configuration, there should be a field for VM Options where you can pass that in.
That would be the canonical way of handling in Spring.
Delete the line or change the path(example /h2/demo1).
spring.datasource.url=jdbc:h2:file:./h2/demo
because the last configuration is storage on this file.
Just tried with
spring.datasource.password=${pass}
and it works correctly.
You can also use environment variables of format SPRING_DATASOURCE_PASSWORD to define these properties. Spring Boot will resolve them without any addition to application configuration.
Ref: https://docs.spring.io/spring-boot/docs/2.2.3.RELEASE/reference/htmlsingle/#boot-features-external-config-application-property-files
Related
I am updating a web app from Spring Boot 2.2.4 to 2.5.6 and have used Open Rewrite to handle the tedium. The run configuration in IntelliJ has spring-boot:run -Dspring.profiles.active=local -f pom.xml in the command line (this has been working with 2.2.4). After adding a try/catch to main, to actually learn why I kept getting exit code 1 with no additional info, I found java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.profiles.active' in value "classpath:application-${spring.profiles.active}.properties" to be the root issue.
Apparently passing local on the command line for spring.profiles.active is now ignored… So, I added it to application.yml which gives me a message about how it is invalid to use spring.profiles.active. I found that the currently active profile should now be in config.activate.on-profile.active so I switched to that and then did a project wide search for spring.profiles.active and replaced every single instance with config.activate.on-profile.active.
I ran the project again and again got the message that it could resolve the placeholder spring.profiles.active! I can't for the life of me figure out where that placeholder is coming from since it doesn't exist anywhere in the project now according to IntelliJ, not even in the ReadMe file!
Is there some secret place I need to look to purge this old placeholder from existence?
I ran into the same issue and found a link to a github issue where a workaround was provided.
Create a class:
public class JunitProfileResolver implements ActiveProfilesResolver {
private static final String PROFILE_JUNIT = "test";
#Override
public String[] resolve(Class<?> testClass) {
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, PROFILE_JUNIT);
return new String[] { PROFILE_JUNIT };
}
}
And use it like:
#ActiveProfiles(profiles = "test", resolver = JunitProfileResolver.class)
public class MyTest {
For me this solves the problem.
Problem:
I have 3 parts in the software:
Client A service
Client B service
Target C service
I want to connect to C from A and B
I wrote a library with following setup:
/src/main/java/pkg.../TargetConnector.java
/src/main/java/pkg.../TargetConfig.java
/src/main/resources/application-dev.properties
/src/main/resources/application-tst.properties
/src/main/resources/application-prd.properties
My clients A and B both have there own sources and properties:
/src/main/java/pkg.../Client{A/B}Service.java
/src/main/java/pkg.../Client{A/B}Config.java
/src/main/resources/application-dev.properties
/src/main/resources/application-tst.properties
/src/main/resources/application-prd.properties
The properties of the Connector contains some login info for the service e.g.
target.url=https://....
target.usr=blablabla
target.key=mySHAkey
which is used in the TargetConfig to preconfigure the Connector e.g.
#Value("target.url")
String url;
#Value("target.usr")
String usr;
#Value("target.key")
String key;
#Bean
public TargetConnector connector() {
return new TargetConnector(url, usr, key);
}
Now when I use the connector jar in the client I can find the configuration via packagescan. The connector class is loaded but the problem is that it does not load the properties files.
Research
I found that multiple property files cannot have the same name (e.g. clients application-{profile}.properties clashes with the one from the connector), so I tried to rename application-{profile}.properties of the targetConnector to application-connector-{profile}.properties.
The properties whoever still do not get loaded, (which makes sense since I do not have a e.g connector-dev profile but my profile is simply named dev).
Furthermore, even if I try to explicitly load one of the property files from the connector with:
#PropertySource({"classpath*:application-connector-dev.properties"})
it cannot be found
Question
My question is actually 3 tiered:
How can I load a property file in a dependency jar at all?
How can I load the profiled version of the property file if the the properties file has a different name than application.properties? e.g. application-connector.properties
How can i combine the answers from question 1 and 2 to load the profiled version of the property in the jar?
If further explanation is needed, please ask.
Answer
I went for an approach as given in the accepted answer.
I Just created 3 configs for the dev, tst, prd profiles containing the values needed and annotated the config files with the correct profiles.
You are using #Configuration annotated class. Maybe you can have one per profile. Here you are an example:
#Configuration
#Profile("profileA")
#PropertySource({"classpath:application-profileA.properties"})
public class ConfigurationProfileA{
#Value("${target.url}")
String url;
#Value("${target.usr}")
String usr;
#Value("${target.key}")
String key;
#Bean
public TargetConnector connector() {
return new TargetConnector(url, usr, key);
}
}
Do the same for profile B (maybe you can structure this better but the key points here are the annotation #Profile("") and #PropertySource(""))
Once you have your config class, Spring will use the Configuration class you want by just filling -spring.profiles.active=profileA (or the name of the profile you have written in the #Profile("") annotation)
I think there is a typo in this line #PropertySource({"classpath*:application-connector-dev.properties"})
Please check by removing the asterik.
In order to run with a specific profile, you can run with option -spring.profiles.active=dev for example
If you don’t run with a profile, it will load the default profile in application.properties that you don’t seem to have.
Furthermore, an advice would be to always have an application.properties and put in it the common properties and the default values that you would override in other properties files.
Other mistake is how you assign properties with #Value annotation, you need to use #Value("${PROPERTY_FROM_PROPERTIES_FILE}")
I want to use a value from application.properties with the #WithUserDetails() annotation for my tests.
My current code:
#Value("${user.admin}")
private String ADMIN;
#Test
#WithUserDetails(ADMIN)
public void foo() {
}
displays the error "Attribute value must be constant"
I am using junit4 with spring runner
Seems this cannot be done, the Java compiler won't allow it as it doesn't consider the value from #Value() to be a constant at compile time.
java has built in capabilities to read a .properties file and JUnit has built in capabilities to run setup code before executing a test suite.
java reading properties:
Properties p = new Properties();
p.load(new FileReader(new File("application.properties")));
junit startup documentation
put those 2 together and you should have what you need.
But yes the minimum requirment is you have application.properties file in your test->resources package as well.
I want to use a yml configuration file in my project. I am using jackson-dataformat-yaml for parsing yml files. But I need to parse yml comments as well. I used the similar approach in python using ruamel yaml. How can I do the same in java?
Upd.
What for? Well, I wanted to make it possible to override my configuration options by using command line arguments. So, to generate description message for each option, I wanted to use my comments. Like this:
In my config.yml
# Define a source directory
src: '/foo/bar'
# Define a destination directory
dst: '/foo/baz'
So when you run your program with the --help flag, you'll see the following output:
Your program can be ran with the following options:
--src Define a source directory
--dst Define a destination directory
The main benefit in such a model is that you don't ever need to repeat the same statement twice, because they can be retrieved from the configuration file.
Basically, you have three layers of data:
Your configuration schema. This defines the values that are to be defined in the configuration file.
The configuration file itself, which describes the usual configuration on the current machine.
One-time switches, which override the usual configuration.
The descriptions of what each value does belong to the schema, not to the configuration file itself. Think about it: If someone edits the configuration file on their machine and changes the comments, your help output would suddenly show different descriptions.
My suggestion would be to add the descriptions to the schema. The schema is the Java class you load your YAML into. I am not sure why you are using Jackson, since it uses SnakeYaml as parser and SnakeYaml is perfectly able to deserialize into Java classes, but has more configuration options since it does not generalize over JSON and YAML like Jackson does.
Here's a general idea how to do it with SnakeYaml (beware, untested):
// ConfigParam.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface ConfigParam { String description(); }
// Configuration.java
public class Configuration {
#ConfigParam("Define a source directory")
String src;
#ConfigParam("Define a destination directory")
String dst;
}
// loading code
Yaml yaml = new Yaml(new Constructor(Configuration.class));
Configuration config = yaml.loadAs(input, Configuration.class);
// help generation code
System.out.println("Your program can be ran with the following options:")
for (Field field: Configuration.class.getFields()) {
ConfigParam ann = field.getAnnotation(ConfigParam.class);
if (ann != null) {
System.out.println(String.format("--%s %s", field.getName(), ann.description());
}
}
For mapping actual parameters to the configuration, you can also loop over class fields and map the parameters to the field names after having loaded the configuration (to replace the standard values with the given ones).
I have two different environments that need different property values and have researched that the proper way to do this is using spring profiles. As such I have setup two different files:
application.properties
application-dev.properties
The run-time environment is setup and includes the following:
spring.profiles.active=dev
Inside the application.properties file a property "foo.bar" set where:
foo.bar=defaultProp
in the applications-dev.properties file
foo.bar=devProp
Inside the app the following code is included:
#Value("${foo.bar}")
String foobar;
#Autowired
Environment env;
When I run the app with the following:
String x = env.getProperty("spring.profiles.active");
x reports the value of "dev" (working as expected)
BUT
foobar reports its value as "defaultProp"
Both the applications.properties and applications-dev.properties are located together in the same directory.
Later the following was included for testing:
for(String s: env.getActiveProfiles())
{
logger.info("Act:" + s);
}
for(String s: env.getDefaultProfiles())
{
logger.info("Def:" + s);
}
with the following output:
Act:dev
Def:default
Again this was also expected and appears to be working
Finally, another variable was inserted into application-dev.properties
ding=dong
and in the app the following code was wired in
#Value("${ding}")
String dingvalue;
Low and behold the value of "dingvalue" reports as "dong", again this is working as expected -- values are being picked up from the application-dev.properties file!
Hence it appears that the active profile is actually being set to "dev" and values are being picked up as expected.
Finally, according the doc.spring.io:
23.4 Profile-specific properties
In addition to application.properties files, profile-specific
properties can also be defined using the naming convention
application-{profile}.properties.
Profile specific properties are loaded from the same locations as
standard application.properties, with profile-specific files always
overriding the default ones irrespective of whether the
profile-specific files are inside or outside your packaged jar.
Can anyone please explain why the application.properties value is not being overwritten by the application-dev.properties as per the documentation?
It is working as expected.
/product-model-configuration/src/main/resources/product-model.yml:
foo:
bar: defaultProp
/product-model-configuration/src/main/resources/product-model-loc-isolated.yml:
foo:
bar: bar