diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml
index f257ee306e..b11c2c9afb 100644
--- a/spring-boot-modules/pom.xml
+++ b/spring-boot-modules/pom.xml
@@ -55,6 +55,7 @@
spring-boot-mvc-2
spring-boot-mvc-3
spring-boot-mvc-4
+ spring-boot-mvc-5
spring-boot-mvc-birt
spring-boot-mvc-jersey
spring-boot-nashorn
diff --git a/spring-boot-modules/spring-boot-mvc-5/README.md b/spring-boot-modules/spring-boot-mvc-5/README.md
new file mode 100644
index 0000000000..7cc3f8a1fe
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/README.md
@@ -0,0 +1,5 @@
+## Spring Boot MVC
+
+This module contains articles about Spring Web MVC in Spring Boot projects.
+
+### Relevant Articles:
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-5/extra.properties b/spring-boot-modules/spring-boot-mvc-5/extra.properties
new file mode 100644
index 0000000000..97837513b1
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/extra.properties
@@ -0,0 +1,2 @@
+endpoint.foo=true
+endpoint.regex=.*
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-5/pom.xml b/spring-boot-modules/spring-boot-mvc-5/pom.xml
new file mode 100644
index 0000000000..61b27ea8f1
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/pom.xml
@@ -0,0 +1,73 @@
+
+
+ 4.0.0
+ spring-boot-mvc-5
+ spring-boot-mvc-5
+ jar
+ Module For Spring Boot MVC Web
+
+
+ com.baeldung.spring-boot-modules
+ spring-boot-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.cloud
+ spring-cloud-starter
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+ commons-configuration
+ commons-configuration
+ 1.10
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ ${start-class}
+ JAR
+
+
+
+
+
+
+ 3.0.0
+ com.baeldung.springboot.swagger.ArticleApplication
+ 2021.0.5
+
+
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/DynamicEndpointApp.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/DynamicEndpointApp.java
new file mode 100644
index 0000000000..cbfa7d8a06
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/DynamicEndpointApp.java
@@ -0,0 +1,47 @@
+package com.baeldung.dynamicendpoints;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+import com.baeldung.dynamicendpoints.config.ReloadableProperties;
+
+@SpringBootApplication
+@EnableWebMvc
+public class DynamicEndpointApp {
+ public static void main(String[] args) {
+ SpringApplication.run(DynamicEndpointApp.class, args);
+ }
+
+
+@Bean
+@ConditionalOnProperty(name = "dynamic.endpoint.config.location", matchIfMissing = false)
+public PropertiesConfiguration propertiesConfiguration(
+ @Value("${dynamic.endpoint.config.location}") String path,
+ @Value("${spring.properties.refreshDelay}") long refreshDelay) throws Exception {
+ String filePath = path.substring("file:".length());
+ PropertiesConfiguration configuration = new PropertiesConfiguration(new File(filePath).getCanonicalPath());
+ FileChangedReloadingStrategy fileChangedReloadingStrategy = new FileChangedReloadingStrategy();
+ fileChangedReloadingStrategy.setRefreshDelay(refreshDelay);
+ configuration.setReloadingStrategy(fileChangedReloadingStrategy);
+ return configuration;
+}
+
+ @Bean
+ @ConditionalOnBean(PropertiesConfiguration.class)
+ @Primary
+ public Properties properties(PropertiesConfiguration propertiesConfiguration) throws Exception {
+ ReloadableProperties properties = new ReloadableProperties(propertiesConfiguration);
+ return properties;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/EndpointRefreshConfigBean.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/EndpointRefreshConfigBean.java
new file mode 100644
index 0000000000..1ea9febfca
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/EndpointRefreshConfigBean.java
@@ -0,0 +1,35 @@
+package com.baeldung.dynamicendpoints.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+@Component
+@RefreshScope
+public class EndpointRefreshConfigBean {
+
+ private boolean foo;
+
+ private String regex;
+
+ public EndpointRefreshConfigBean(@Value("${endpoint.foo}") boolean foo, @Value("${endpoint.regex}") String regex) {
+ this.foo = foo;
+ this.regex = regex;
+ }
+
+ public boolean isFoo() {
+ return foo;
+ }
+
+ public void setFoo(boolean foo) {
+ this.foo = foo;
+ }
+
+ public String getRegex() {
+ return regex;
+ }
+
+ public void setRegex(String regex) {
+ this.regex = regex;
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/EnvironmentConfigBean.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/EnvironmentConfigBean.java
new file mode 100644
index 0000000000..f89341d3f5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/EnvironmentConfigBean.java
@@ -0,0 +1,27 @@
+package com.baeldung.dynamicendpoints.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+
+@Component
+public class EnvironmentConfigBean {
+
+ private final Environment environment;
+
+ public EnvironmentConfigBean(@Autowired Environment environment) {
+ this.environment = environment;
+ }
+
+ public String getEndpointRegex() {
+ return environment.getProperty("endpoint.regex");
+ }
+
+ public boolean isFooEndpointEnabled() {
+ return Boolean.parseBoolean(environment.getProperty("endpoint.foo"));
+ }
+
+ public Environment getEnvironment() {
+ return environment;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/PropertiesException.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/PropertiesException.java
new file mode 100644
index 0000000000..67d634f341
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/PropertiesException.java
@@ -0,0 +1,10 @@
+package com.baeldung.dynamicendpoints.config;
+
+public class PropertiesException extends RuntimeException {
+ public PropertiesException() {
+ }
+
+ public PropertiesException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadableProperties.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadableProperties.java
new file mode 100644
index 0000000000..ef44863b17
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadableProperties.java
@@ -0,0 +1,23 @@
+package com.baeldung.dynamicendpoints.config;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.commons.configuration.PropertiesConfiguration;
+
+public class ReloadableProperties extends Properties {
+ private PropertiesConfiguration propertiesConfiguration;
+
+ public ReloadableProperties(PropertiesConfiguration propertiesConfiguration) throws IOException {
+ super.load(new FileReader(propertiesConfiguration.getFile()));
+ this.propertiesConfiguration = propertiesConfiguration;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ String val = propertiesConfiguration.getString(key);
+ super.setProperty(key, val);
+ return val;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadablePropertySource.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadablePropertySource.java
new file mode 100644
index 0000000000..92cb4c3e55
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadablePropertySource.java
@@ -0,0 +1,32 @@
+package com.baeldung.dynamicendpoints.config;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
+import org.springframework.core.env.PropertySource;
+import org.springframework.util.StringUtils;
+
+public class ReloadablePropertySource extends PropertySource {
+
+ private final PropertiesConfiguration propertiesConfiguration;
+
+ public ReloadablePropertySource(String name, PropertiesConfiguration propertiesConfiguration) {
+ super(name);
+ this.propertiesConfiguration = propertiesConfiguration;
+ }
+
+ public ReloadablePropertySource(String name, String path) throws ConfigurationException {
+ super(StringUtils.hasText(name) ? path : name);
+ try {
+ this.propertiesConfiguration = new PropertiesConfiguration(path);
+ this.propertiesConfiguration.setReloadingStrategy(new FileChangedReloadingStrategy());
+ } catch (PropertiesException e) {
+ throw e;
+ }
+ }
+
+ @Override
+ public Object getProperty(String s) {
+ return propertiesConfiguration.getProperty(s);
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadablePropertySourceConfig.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadablePropertySourceConfig.java
new file mode 100644
index 0000000000..24864b8c78
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/config/ReloadablePropertySourceConfig.java
@@ -0,0 +1,28 @@
+package com.baeldung.dynamicendpoints.config;
+
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MutablePropertySources;
+
+@Configuration
+public class ReloadablePropertySourceConfig {
+
+ private ConfigurableEnvironment env;
+
+ public ReloadablePropertySourceConfig(@Autowired ConfigurableEnvironment env) {
+ this.env = env;
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = "dynamic.endpoint.config.location", matchIfMissing = false)
+ public ReloadablePropertySource reloadablePropertySource(PropertiesConfiguration properties) {
+ ReloadablePropertySource reloadablePropertySource = new ReloadablePropertySource("toggle-endpoints", properties);
+ MutablePropertySources sources = env.getPropertySources();
+ sources.addFirst(reloadablePropertySource);
+ return reloadablePropertySource;
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/controller/AppController.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/controller/AppController.java
new file mode 100644
index 0000000000..e39d893a7c
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/controller/AppController.java
@@ -0,0 +1,58 @@
+package com.baeldung.dynamicendpoints.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.dynamicendpoints.filter.DynamicEndpointFilter;
+import com.baeldung.dynamicendpoints.config.EndpointRefreshConfigBean;
+import com.baeldung.dynamicendpoints.config.EnvironmentConfigBean;
+
+@RestController
+@RequestMapping("/")
+public class AppController {
+
+ private EndpointRefreshConfigBean endpointRefreshConfigBean;
+ private EnvironmentConfigBean environmentConfigBean;
+
+ @Autowired
+ public AppController(EndpointRefreshConfigBean endpointRefreshConfigBean, EnvironmentConfigBean environmentConfigBean) {
+ this.endpointRefreshConfigBean = endpointRefreshConfigBean;
+ this.environmentConfigBean = environmentConfigBean;
+ }
+
+ @GetMapping("/foo")
+ public ResponseEntity fooHandler() {
+ if (endpointRefreshConfigBean.isFoo()) {
+ return ResponseEntity.status(200)
+ .body("foo");
+ } else {
+ return ResponseEntity.status(503)
+ .body("endpoint is unavailable");
+ }
+ }
+
+ @GetMapping("/bar1")
+ public String bar1Handler() {
+ return "bar1";
+ }
+
+ @GetMapping("/bar2")
+ public String bar2Handler() {
+ return "bar2";
+ }
+
+ @Bean
+ @ConditionalOnBean(EnvironmentConfigBean.class)
+ public FilterRegistrationBean dynamicEndpointFilterFilterRegistrationBean(EnvironmentConfigBean environmentConfigBean) {
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
+ registrationBean.setFilter(new DynamicEndpointFilter(environmentConfigBean.getEnvironment()));
+ registrationBean.addUrlPatterns("*");
+ return registrationBean;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/filter/DynamicEndpointFilter.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/filter/DynamicEndpointFilter.java
new file mode 100644
index 0000000000..7ec8a1f42b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/dynamicendpoints/filter/DynamicEndpointFilter.java
@@ -0,0 +1,38 @@
+package com.baeldung.dynamicendpoints.filter;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.core.env.Environment;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+public class DynamicEndpointFilter extends OncePerRequestFilter {
+
+ private Environment environment;
+
+ public DynamicEndpointFilter(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ String path = request.getRequestURI();
+ String regex = this.environment.getProperty("endpoint.regex");
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(path);
+ boolean matches = matcher.matches();
+
+ if (!matches) {
+ response.sendError(HttpStatus.SERVICE_UNAVAILABLE.value(), "Service is unavailable");
+ } else {
+ filterChain.doFilter(request, response);
+ }
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc-5/src/main/resources/application.properties
new file mode 100644
index 0000000000..d8bacac436
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/resources/application.properties
@@ -0,0 +1,12 @@
+server.port=9090
+management.server.port=8081
+management.server.address=127.0.0.1
+
+management.endpoints.web.exposure.include=*
+
+logging.level.org.springframework=INFO
+
+#Dynamic Endpoint
+spring.main.allow-bean-definition-overriding=true
+dynamic.endpoint.config.location=file:extra.properties
+spring.properties.refreshDelay=1
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/resources/logback.xml b/spring-boot-modules/spring-boot-mvc-5/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-5/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file