Spring Boot Externalizing properties not working - java

I have looked at the below threads and followed things given there. Still my property override is not happening
Spring Boot - Externalized properties
Profile Specific Property Enablement
Spring Boot External Config
I am on Tomcat 8.0.33 and Spring boot starter web and got this in my setenv.sh
export JAVA_OPTS="$JAVA_OPTS -Dlog.level=INFO -Dspring.config.location=file:/opt/jboss/apache-tomcat-8.0.33/overrides/ -Dspring.profiles.active=dev"
And in the overrides folder I got 2 files
1) application.properties
2) application-dev.properties
The application.properties has a single entry in it
spring.profiles.active=dev
I see that the proper log.level is fed to my code which means this command is working. Its just that I am clueless as to why my override is not happening as expected
I don't have any `PropertyPlaceholderConfigurer code in my workspace. I am not even sure if I need 1

I don't use this method to externalise properties. First, I'll try a suggestion for your method and then I'll show you what I'm using.
The suggestion for your method is to use file:/// instead of file:/ as with Spring I found that when not passing the three slashes after the colon it didn't recognise the property.
I've created a sample project for you, available here with instructions.
Now for the method I use.
I define a Configuration file for each profile and I keep the application.properties file under src/main/resources.
Then I use the #Profile and #PropertySource annotations on each configuration file.
For example:
#Configuration
#Profile("dev")
#PropertySource("file:///${user.home}/.devopsbuddy/application-dev.properties")
public class DevelopmentConfig {
#Bean
public EmailService emailService() {
return new MockEmailService();
}
#Bean
public ServletRegistrationBean h2ConsoleServletRegistration() {
ServletRegistrationBean bean = new ServletRegistrationBean(new WebServlet());
bean.addUrlMappings("/console/*");
return bean;
}
}
And
#Configuration
#Profile("prod")
#PropertySource("file:///${user.home}/.devopsbuddy/application-prod.properties")
public class ProductionConfig {
#Bean
public EmailService emailService() {
return new SmtpEmailService();
}
}
I have also got a Configuration file that is valid for all profiles, which I call ApplicationConfig, as follows:
#Configuration
#EnableJpaRepositories(basePackages = "com.devopsbuddy.backend.persistence.repositories")
#EntityScan(basePackages = "com.devopsbuddy.backend.persistence.domain.backend")
#EnableTransactionManagement
#PropertySource("file:///${user.home}/.devopsbuddy/application-common.properties")
public class ApplicationConfig {
}
My src/main/resources/application.properties file looks like the following:
spring.profiles.active=dev
default.to.address=me#example.com
token.expiration.length.minutes=120
Of course I could externalise the spring.profile.active property by passing it as a system property but for my case and for now it's fine.
When running the application, if I pass the "dev" profile, Spring will load all properties and Beans defined in the DevelopmentConfig class plus all those in ApplicationConfig. If I pass "prod", the ProductionConfig and ApplicationConfig properties will be loaded instead.
I'm completing a course on how to create a Spring Boot website with Security, Email, Data JPA, Amazon Web Services, Stripe and much more. If you want, you can register your interest here and you will get notified when the course is open for enrolment.

Related

Spring Cloud Config: Call a bean from bootstrap.properties/yml

Is it possible to call a bean from bootstrap.properties?
I'm trying to implement a Cloud Config Client.
The Bean is similar to:
#Bean
public MyObject myObject(String environment) {
return new MyObject(environment);
}
....
public class MyObject {
private String environment;
// getters setters
}
In the bootstrap.properties file I have the following line:
spring.profiles.active= #Here I should get the value from the bean
Is it possible to write something like:
spring.profiles.active= ${myObject.environment}
Thank you very much.
You can, plugging into spring.factories:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.demo.MyBootstrapConfiguration
And then write a normal Spring Java Configuration Object
#Configuration
public class MyBootstrapConfiguration {
// normal spring java config
}
Based on the docs, the bootstrap file:
Out of the box it is responsible for loading configuration properties
from the external sources, and also decrypting properties in the local
external configuration files
and the its content is meant to be referenced in the beans, not the other way round.
If you want to pass the active profile somehow to it, based on the docs you can:
1) Use -D option while starting the app:
java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
2) Set SPRING_PROFILES_ACTIVE property on your OS environment.

#Value not working in Spring #Configuration

Need help, where is the issue?
I have a configuration class which is loading properties as
WebConfig.java
#Configuration
#PropertySource(value={"classpath:application.properties"})
class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
I have another configuration class where I am trying to use the properties as
MyServerConfig.java
#Configuration
class MyServerConfig {
#Value("${server.url}")
private String url;
...
}
application.properties
server.url=http://localhost:8080/test/abc
But getting:
java.lang.IllegalArgumentException: Could not resolve placeholder 'server.url'.
Don't know what is missing here? Any thoughts?
Use the #PropertyScan annotation in the class where a certain property will be used:
#Configuration
#PropertyScan("classpath:application.properties")
class MyServerConfig {
#Value( "${server.url}" )
private String url;
}
For getting the values for your #Value variables, the application.properties is not needed to be configured in any special way because this file is always scanned. So remove the #PropertySource annotation and PropertySourcesPlaceholderConfigurer bean.
These are used if you want to add other .properties files (e.g. constants.properties, db-config.properties).
So just remove those and try to run your application again
Very important:
Make sure you scan the class that uses the #Value annotation (If your BootApplication is in some package instead of the 'main' package, add the proper #SpringBootApplication(scanBasePackages = { "com.my.project" }) annotation).
Make sure your application.properties is on your classpath.
Bonus If you are using spring profiles (e.g: prod, dev), you can also have application-prod.properties and application-dev.properties files that will be scanned and included depending on which profile you are running.

Loading additional spring profiles from java config

Is there any possibility to load aditional spring profiles from java config?
I know that I can use -Dspring.profile.active argument and also add profiles to spring.profiles.include in application.properties.
What I need is to be able to activate profiles from java config. I've created PropertyPlaceholderConfigurer, where I'm adding some custom property files, which also contains property spring.profiles.include, all properties are load and it works ok, but spring doesn't activate any profiles which are inclded using this property.
#Bean
public static PropertyPlaceholderConfigurer ppc() throws IOException {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocations(new ClassPathResource("properties/" + property + ".properties"));
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
The active spring profiles are defined in properties via the following configuration: spring.profiles.active:.
You should list in all the files that you import the profiles that they activate via the above configuration key.
EDIT
First, as per the official documentation the configuration spring.profiles.include is more suitable for unconditionally adding active profiles.
Second, I can assume that PropertyPlaceholderConfigurer is not suitable for what you want to achieve. The official documentation lists the ways you can Externalize Configuration. You can try to use #PropertySource:
#PropertySources({
#PropertySource(value = "classpath:application.properties"),
#PropertySource(value = "classpath:other.properties", ignoreResourceNotFound = true)
})
public class Application {
...
}
}
Additionally, you can try to list the other properties files in property spring.config.location inside application.properties as described here.

How to force the import/loading of rest controller programatically

I'm starting a spring boot rest service which may load different packages depending on the distribution. This means sometimes the distribution will contain some jars where certain REST controllers are, sometimes this controllers are not there.
So How I'm able to tell spring-boot where to find the controllers with a configuration files. Now I'm sending this info by annotations forcing me to create a "main" per distribution. I will like to define a unique main that imports the controllers defined in a file. In other words I want to access the #Importannotation manually as is shown in the sniped bellow:
#Configuration
#PropertySource("conf.cfg")
#Import(value = {RestContorller1.class, RestContorller2.class})
#EnableAutoConfiguration
#ConfigurationProperties
#SpringBootApplication
#RestController
#EnableSwagger2
public class Application {
public static void main(String[] args) {
String confFile = Const.DEFAULT_CONFIGURATION_FILE;
if(args.length>0)
confFile= args[0];
System.setProperty("spring.config.name",confFile);
Boolean hasStarted = DataProcessingCore.start(confFile);
if(hasStarted) {
SpringApplication springApp = new SpringApplication(Application.class);
try {
springApp.setDefaultProperties(Utils.createPropertyFiles(confFile));
} catch (IOException e) {
e.printStackTrace();
}
springApp.addInitializers();
springApp.run(args);
}
}
}
If I understand you corretly, your controllers reside in a JAR imported by maven/gradle to your main project/s.
In order to create auto-configuration like spring boot does, that in the same way can be used to import your controllers when the jar is in the classpath, you can tell spring to find your custom configuration on start-up.
I wrote a simple example for that here: Creating your own auto-configuration
In pricipal, you create a spring-boot application (without the spring boot maven plugin!! it is important for the classpath and packaging). and create a file named spring.factories (you can find the actual content in the guide I linked) that tells any spring-boot application that have this jar to load your configuration that may do a #ComponentScan to search for your controllers or set the #Bean manually.
Doing that, you don't have to do #Import and the controllers will be loaded dynamically.

Spring Boot ignores spring.config.name/location properties

I've got quite a simple application.yml file:
spring:
config:
name: android,ios,test,web
I expected to gain an ability to name config files like android.yml and put them into
classpath:/,classpath:/config/,file:./,file:./config/
as the DEFAULT_SEARCH_LOCATIONS constant from the ConfigFileApplicationListener class specifies. I created a file in the same directory with the main config:
android:
clientId: 0
clientSecret: clientSecret
Then I wrote a #Configuration class with one method to get an instance of ClientDetails by the #ConfigurationProperties:
#Configuration
public class TrustedClientInformationConfiguration {
#Bean(name = ANDROID)
#ConfigurationProperties(prefix = ANDROID)
public ClientDetails getAndroidClientDetails() {
return new BaseClientDetails();
}
}
Unfortunately, after autowiring, I got the instance with unfilled fields. What have I missed?
EDIT1: I found and debugged a method where CONFIG_NAME_PROPERTY = "spring.config.name" is used (it's only one usage), the containsProperty condition always returns false:
private Set<String> getSearchNames() {
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
null);
}
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}
EDIT2: It is a try to create an oauth2 client configuration by moving properties to a separate file for each trusted client. They should be always instantiated despite the active profile.
The spring.config properties should be set on the command-line as they're needed before any config files are loaded.
However it looks like the feature you actually want is profiles. They're a much easier way to organise different configuration for different environments. Files can be named application-android, application-test, etc.
Documentation

Categories