change application.properties value in spring boot application using #RefreshScope to take effect during runtime - spring

I have a spring boot application that reads a value from a property from application.properties file in the Spring Boot Application. I want to use it as a switch and need to be able to change it during run time so that the code will switch as per the value configured to get the desired output.
As of now it is requiring a restart of the application. Here is what I am using:
#Configuration
#RefreshScope
#PropertySource("classpath:application.properties")
#Service
public class MyServiceImpl implements MyService {
#Value("${enabled}")
private boolean SWITCH_ENABLED;
if (SWITCH_ENABLED==true) {
// code for switch when true
}
else {
// code for switch is false
}
I have the following properties in the application.properties :
enabled=true
The value is not getting refreshed and takes effect only after restart. I tried using the spring-boot-actuator as well.

To take advantage of the #RefreshScope annotation you need to send a refresh to your app. You can spring-cloud-context to your classpath which adds /refresh and /env endpoints.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>1.3.4.RELEASE</version>
</dependency>
Then after you change application.properties or application.yml you can POST to /refresh which will reload the properties. Example:
curl -X POST http://localhost:8080/refresh

Related

Spring boot and AWS Secret Manager - how to feature flaged the listener to set the secrets only on prod but not on test env

I need to integrate my spring boot app with the AWS Secret manager to retrieve the DB credentials from it. It has to work only on prod environment.
This is what I did until now
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-secretsmanager</artifactId>
<version>1.12.131</version>
</dependency>
...
SpringApplication application = new SpringApplication(MyApplication.class);
application.addListeners(new DatabasePropertiesListener());
application.run(args);
And my listener implements the
ApplicationListener<ApplicationPreparedEvent>
Is there any way to run my method inside that listener only on specific environment ( kind of feature flag) ? I need to say that this is to early to use variables from properties files.
Update:
When we have to decide before context creation (.i.e. ApplicationListener is no Component/Bean), then we (just mimic profiles) set:
# env: SET MY_FLAG=foo -> System.getenv("MY_FLAG")
# system: java -jar -Dmy.flag=foo myApp.jar -> System.getProperty("my.flag")
# cmd arg:
java -jar myApp.jar aws
..and issue it in our (spring boot) main method like:
if("aws".equalsIgnoreCase(args[0])) // alternatively: System.get...
application.addListeners(new DatabasePropertiesListener());
Misunderstood answer
Sure we can: With spring (boot) core features!
Assuming our ApplicationListener is a "spring managed bean", we annotate it with:
#Component
#Profile("aws") // custom profile (name)
class MyApplicationListener implements ...
{ ... }
...this will not load this "bean" into our context, unless we define:
spring.profiles.acitve=aws,... # a comma separated list of profiles to activate
Profiles
Spring Profiles provide a way to segregate parts of your application configuration and make it be available only in certain environments. Any #Component (Or #Bean,#Service,#Repository...descendants), #Configuration or #ConfigurationProperties can be marked with #Profile to limit when it is loaded, as shown in the ...
... above example.
An advanced application of #Profile annotation:
with multiple profiles: "or"/"and" semantics
"not" (!) operator
#Profile("!local","aws") // (String[]: OR semantics) the annotated component/configuration/property will
// be loaded, when active profiles NOT contains "local" OR contains "aws"
// for AND semantics, we'd use (1 string): "!local & aws"
Activating Profile(s!)
spring.profiles.acitve can be set/added (like any spring property source) through several (14^^, precisely priorized) locations.
E.g. setting environment variable (5th lowest priority, but higher than application.properties (3rd lowest)):
SPRING_PROFILES_ACTIVE=aws
Or as a command line flag (when starting the application, 11th lowest/3rd highest priority):
java -jar myApp.jar --spring.profiles.active=aws,...
#comma separated list
For (spring) tests additionally exists an #ActiveProfiles annotation.
Remarks/Note
Deciding for profiles, we should ensure to "make it consistently" (not raising Nullpointer/BeaninitalizationExceptions ... with dependencies!;). If needed: Creating replacement/local/test (#Profile("!aws")) beans.
Activating a profile "xyz", automatically tries to load application-xyz.properties (with higher priority than application.properties (prio 3.1 - 3.4))...also in (spring-)cloud-config.
Not to forget: The default profile (activated by spring automatically, only when no explicit profile is activated).
Reference
For detailed documentation, please refer to:
Spring Boot - Externalized Configuration
Spring Boot - Profiles
Spring Environment Abstraction chapter
#Profile Javadoc
#ActiveProfiles Javadoc
Profile sample from Configuration Javadoc:
#Configuration classes may be marked with the #Profile annotation to indicate they should be processed only if a given profile or profiles are active:
#Profile("development")
#Configuration
public class EmbeddedDatabaseConfig {
#Bean
public DataSource dataSource() {
// instantiate, configure and return embedded DataSource
}
}
#Profile("production")
#Configuration
public class ProductionDatabaseConfig {
#Bean
public DataSource dataSource() {
// instantiate, configure and return production DataSource
}
}
Alternatively, you may also declare profile conditions at the #Bean method level — for example, for alternative bean variants within the same configuration class:
#Configuration
public class ProfileDatabaseConfig {
#Bean("dataSource")
#Profile("development")
public DataSource embeddedDatabase() { ... }
#Bean("dataSource")
#Profile("production")
public DataSource productionDatabase() { ... }
}
See the #Profile and Environment javadocs for further details.

"httptrace" endpoint of Spring Boot Actuator doesn't exist anymore with Spring Boot 2.2.0

With Spring Boot 2.2.0 the "httptrace" Actuator endpoint doesn't exist anymore. How can I get this functionality back?
The functionality has been removed by default in Spring Boot 2.2.0.
As a workaround, add this configuration to the Spring environment:
management.endpoints.web.exposure.include: httptrace
and provide an HttpTraceRepository bean like this:
#Configuration
// #Profile("actuator-endpoints")
// if you want: register bean only if profile is set
public class HttpTraceActuatorConfiguration {
#Bean
public HttpTraceRepository httpTraceRepository() {
return new InMemoryHttpTraceRepository();
}
}
http://localhost:8080/actuator/httptrace works again.
You need to enable httptrace by having following application properties. By default it is disabled
management.trace.http.enabled: true
management.endpoints.web.exposure.include: httptrace
and Requires an HttpTraceRepository bean. You can use Your own Custom implementation or InMemoryHttpTraceRepository

Spring Consul - disable for unit tests

Updated
My class listed below (ServiceDiscoveryConfiguration) is never being utilized. Even if I remove the #EnableDiscoveryClient to attempt to completely avoid the setup, it still attempts to connect to Consul.
The only thing that worked for me was removing the Consul Maven depdency completely:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
What can I do to prevent Consul for running for unit tests if not through the profile and annotation setup?
Original
I have an application using Spring Consul.
I have a class set up to enable discovery like this:
#Profile ("!" + Profiles.UNIT_TEST)
#Configuration
#EnableDiscoveryClient
public class ServiceDiscoveryConfiguration {
}
This should be disabling the Consul portion, if I am not mistaken. The base test class (it's an abstract shared between all of my unit tests) is setup up with the following annotations. This is where I think the problem is.
#SpringBootTest (classes = Service.class)
#WebAppConfiguration
#TestExecutionListeners (...)
#DirtiesContext
#ActiveProfiles (Profiles.UNIT_TEST)
#Test (...)
public abstract class AbstractBootTest extends AbstractTestNGSpringContextTests {
// ...
}
When I execute my tests I get:
Caused by: com.ecwid.consul.transport.TransportException:
java.net.ConnectException: Connection refused
This leads me to believe that the profile activation is not working or my syntax using the ! operator on the #Profile specification is not doing what I thought it was supposed to be doing. The root execution class itself has basic annotations including a #ComponentScan annotation that I know has the appropriate packages being scanned.
Assistance?
You can disable via
#TestPropertySource(properties = {"spring.cloud.consul.config.enabled=false"})
if you have bootstrap.properties file in your project, you should create bootstrap.properties file under test/resources:
spring.application.name=<service-name>
spring.cloud.bus.enabled=false
spring.cloud.discovery.enabled=false
spring.cloud.consul.enabled=false
spring.cloud.consul.config.enabled=false
This will disable consul integration in tests
Add below property in your application.properties file under test/resources
spring.cloud.discovery.enabled=false
spring.cloud.consul.enabled=false
spring.cloud.consul.config.enabled=false
This will disable consul integration while testing your application
The problem is that if you have spring-consul in the classpath it will try to auto-configure it anyway
The profile annotation is correct
#Profile ("!" + Profiles.UNIT_TEST)
the profile activation looks ok, also
#ActiveProfiles (Profiles.UNIT_TEST)
You wrote you are using ComponentScan, that is important, because #Profile annotation on a bean is ignored, if the bean is instantiated by a #Bean annotated method. May be you check again, to see, this does not happen ?
To narrow down the problem you can try :
set a breakpoint in the constructor of ServiceDiscoveryConfiguration, to see if it is instantiated or not
remove #EnableDiscoveryClient to see if this is really the cause of your problems

Spring-boot not respecting liquibase properties

I'm in the process of setting up liquibase to manage my database in a new spring boot application. I need the liquibase dependency in my classpath to reset the database state after certain integration tests run. During my tests I do not want liquibase to be enabled via spring auto config during application context initialization. I've tried adding liquibase.enabled = false to the application.properties, however when I debug the LiquibaseAutoConfiguration class it appears that enabled is always set to true.
I'm not new to spring, but I am new to spring-boot's auto configuration. Has anyone had issues with spring boot not respecting properties in application.properties?
My setup is fairly minimal:
Relevant code snippets:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringBootClass.class })
public class databaseTests{
#Before
public void setup() throws LiquibaseException, SQLException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(dataSource.getConnection()));
Liquibase liquibase = new Liquibase("db/changelog/db.changelog-master.yaml", new FileSystemResourceAccessor("src/main/resources/"),database );
liquibase.dropAll();
liquibase.update("test");
}
..
}
#SpringBootApplication
#Import({ DataSourceConfig.class, HibernateConfig.class, OauthConfig.class })
#EnableConfigurationProperties
public class SpringBootClass {
..
}
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.6.RELEASE</version>
<!-- <liquibase.version>3.3.5</liquibase.version> -->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<scope>test</scope>
</dependency>
If you want your tests to consume application.properties you need to run them as a Spring Boot application. Your use of #ContextConfiguration means that you're currently running them as a vanilla Spring Framework application. Replace the #ContextConfiguration annotation with #SpringApplicationConfiguration.
Should have RTFM...
from spring boot documentation
ConfigFileApplicationContextInitializer is an
ApplicationContextInitializer that can apply to your tests to load
Spring Boot application.properties files. You can use this when you
don’t need the full features provided by
#SpringApplicationConfiguration.
#ContextConfiguration(classes = Config.class,
initializers = ConfigFileApplicationContextInitializer.class)
Changing my config to use the initializer worked.

Spring boot - custom variables in Application.properties

I have spring boot client that consumes a restful api. Instead of hardcoding the IP address of the REST API in the java class, is there any key entry in the application.properties I can use?
And if not, can I create a custom entry?
Thanks
The infrastructure that Spring Boot uses can be used in your own project in the exact same way. You commented in #zmitrok answer about a "unknown property" warning. That is because your property has no meta-data so the IDE does not know about it.
I would strongly advice you not to use #Value if you can as it is rather limited compared to what Spring Boot offers (#Value is a Spring Framework feature).
Start by creating some POJO for your IP:
#ConfigurationProperties("app.foo")
public class FooProperties {
/**
* IP of foo service used to blah.
*/
private String ip = 127.0.0.1;
// getter & setter
}
Then you have two choices
Put #Component on FooProperties and enable the processing of configuration properties by adding #EnableConfigurationProperties on any of your #Configuration class (this last step is no longer necessary as of Spring Boot 1.3.0.M3)
Leave FooProperties as is and add #EnableConfigurationProperties(FooProperties.class) to any of your #Configuration class which will create a Spring Bean automatically for you.
Once you've done that app.foo.ip can be used in application.properties and you can #Autowired FooProperties in your code to look for the value of the property
#Component
public MyRestClient {
private final FooProperties fooProperties;
#Autowired
public MyRestClient(FooProperties fooProperties) { ... }
public callFoo() {
String ip = this.fooProperties.getIp();
...
}
}
Okay so your key is still yellow in your IDE. The last step is to add an extra dependency that will look your code and generate the relevant meta-data at build time. Add the following to your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
And voilà, your key is recognized, you have javadoc and the IDE gives you the default value (the value you initialized on the field). Once you've that you can use any type the conversion service handles (i.e. URL) and the javadoc on the field is used to generate documentation for your keys.
You can also add any JSR-303 constraint validation on your field (for instance a regex to check it's a valid ip).
Check this sample project and the documentation for more details.
Instead of hardcoding the IP into the properties file, you can start the application with
-Dmy.property=127.127.10.20
And Spring Boot will automatically pick it up with
#Value("${my.property}")
private String myProperty;
You can add your own entries to the application.properties. Just make sure that the property name does not clash with the common properties listed at http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#common-application-properties

Resources