diff --git a/spring-boot-modules/spring-boot-actuator/README.md b/spring-boot-modules/spring-boot-actuator/README.md
index 59f7e929da..ea43377ed2 100644
--- a/spring-boot-modules/spring-boot-actuator/README.md
+++ b/spring-boot-modules/spring-boot-actuator/README.md
@@ -12,3 +12,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Health Indicators in Spring Boot](https://www.baeldung.com/spring-boot-health-indicators)
- [How to Enable All Endpoints in Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuator-enable-endpoints)
- [Spring Boot Startup Actuator Endpoint](https://www.baeldung.com/spring-boot-actuator-startup)
+- [Metrics for your Spring REST API](https://www.baeldung.com/spring-rest-api-metrics)
diff --git a/spring-boot-modules/spring-boot-actuator/pom.xml b/spring-boot-modules/spring-boot-actuator/pom.xml
index b2c7a4d28e..1ccf436bbf 100644
--- a/spring-boot-modules/spring-boot-actuator/pom.xml
+++ b/spring-boot-modules/spring-boot-actuator/pom.xml
@@ -23,6 +23,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.apache.tomcat.embed
+ tomcat-embed-jasper
+
org.springframework.boot
spring-boot-starter-data-jpa
@@ -35,6 +39,16 @@
com.h2database
h2
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+ javax.servlet
+ jstl
+ runtime
+
org.springframework.boot
spring-boot-starter-test
@@ -51,6 +65,10 @@
spring-security-test
test
+
+ org.awaitility
+ awaitility
+
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java
new file mode 100644
index 0000000000..729b3c0b96
--- /dev/null
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java
@@ -0,0 +1,42 @@
+package com.baeldung.metrics;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.web.context.request.RequestContextListener;
+
+import javax.servlet.ServletContext;
+
+@EnableScheduling
+@ComponentScan("com.baeldung.metrics")
+@SpringBootApplication
+public class MetricsApplication extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ return application.sources(MetricsApplication.class);
+ }
+
+ @Override
+ public void onStartup(ServletContext sc) {
+ // Manages the lifecycle of the root application context
+ sc.addListener(new RequestContextListener());
+ }
+
+ public static void main(final String[] args) {
+ // only load properties for this application
+ System.setProperty("spring.config.location", "classpath:application-metrics.properties");
+ SpringApplication.run(MetricsApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/spring/WebConfig.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/WebConfig.java
similarity index 81%
rename from spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/spring/WebConfig.java
rename to spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/WebConfig.java
index e35acb0bf0..4c38e4dbad 100644
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/spring/WebConfig.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/WebConfig.java
@@ -1,4 +1,4 @@
-package com.baeldung.spring;
+package com.baeldung.metrics;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@@ -10,14 +10,10 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
-@ComponentScan("com.baeldung.web")
+@ComponentScan("com.baeldung.metrics")
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
- public WebConfig() {
- super();
- }
-
@Bean
public ViewResolver viewResolver() {
final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
@@ -26,11 +22,10 @@ public class WebConfig implements WebMvcConfigurer {
return viewResolver;
}
- // API
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
- registry.addViewController("/graph.html");
- registry.addViewController("/homepage.html");
+ registry.addViewController("/metrics/graph.html");
+ registry.addViewController("/metrics/homepage.html");
}
}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/controller/MetricsController.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/controller/MetricsController.java
new file mode 100644
index 0000000000..e52ddd70f1
--- /dev/null
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/controller/MetricsController.java
@@ -0,0 +1,41 @@
+package com.baeldung.metrics.controller;
+
+import com.baeldung.metrics.service.InMemoryMetricService;
+import com.baeldung.metrics.service.MetricService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.Map;
+
+@Controller
+@RequestMapping(value = "/metrics")
+@ResponseBody
+public class MetricsController {
+
+ @Autowired
+ private InMemoryMetricService metricService;
+
+ // change the qualifier to use the in-memory implementation
+ @Autowired
+ @Qualifier("customActuatorMetricService")
+ private MetricService graphMetricService;
+
+ @GetMapping(value = "/metric")
+ public Map> getMetric() {
+ return metricService.getFullMetric();
+ }
+
+ @GetMapping(value = "/status-metric")
+ public Map getStatusMetric() {
+ return metricService.getStatusMetric();
+ }
+
+ @GetMapping(value = "/metric-graph-data")
+ public Object[][] getMetricData() {
+ return graphMetricService.getGraphData();
+ }
+}
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/MetricFilter.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java
similarity index 64%
rename from spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/MetricFilter.java
rename to spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java
index dee63b226f..0f7579f060 100644
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/MetricFilter.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java
@@ -1,4 +1,11 @@
-package com.baeldung.web.metric;
+package com.baeldung.metrics.filter;
+
+import com.baeldung.metrics.service.CustomActuatorMetricService;
+import com.baeldung.metrics.service.InMemoryMetricService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -9,24 +16,23 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.support.WebApplicationContextUtils;
-
@Component
public class MetricFilter implements Filter {
@Autowired
- private IMetricService metricService;
+ private InMemoryMetricService metricService;
@Autowired
- private ICustomActuatorMetricService actMetricService;
+ private CustomActuatorMetricService actMetricService;
@Override
- public void init(final FilterConfig config) throws ServletException {
+ public void init(final FilterConfig config) {
if (metricService == null || actMetricService == null) {
- metricService = (IMetricService) WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext()).getBean("metricService");
- actMetricService = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext()).getBean(CustomActuatorMetricService.class);
+ WebApplicationContext appContext = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(config.getServletContext());
+
+ metricService = appContext.getBean(InMemoryMetricService.class);
+ actMetricService = appContext.getBean(CustomActuatorMetricService.class);
}
}
@@ -42,8 +48,4 @@ public class MetricFilter implements Filter {
actMetricService.increaseCount(status);
}
- @Override
- public void destroy() {
-
- }
}
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/ActuatorMetricService.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/ActuatorMetricService.java
similarity index 61%
rename from spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/ActuatorMetricService.java
rename to spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/ActuatorMetricService.java
index 8c26fa04a0..3eef265c02 100644
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/ActuatorMetricService.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/ActuatorMetricService.java
@@ -1,32 +1,31 @@
-package com.baeldung.web.metric;
+package com.baeldung.metrics.service;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.Meter;
+import io.micrometer.core.instrument.MeterRegistry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Service;
-
-import io.micrometer.core.instrument.Counter;
-import io.micrometer.core.instrument.Meter;
-import io.micrometer.core.instrument.MeterRegistry;
-
@Service
-public class ActuatorMetricService implements IActuatorMetricService {
+public class ActuatorMetricService implements MetricService {
+
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
@Autowired
private MeterRegistry publicMetrics;
- private final List> statusMetricsByMinute;
+ private final List> statusMetricsByMinute;
private final List statusList;
- private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
public ActuatorMetricService() {
- super();
- statusMetricsByMinute = new ArrayList>();
- statusList = new ArrayList();
+ statusMetricsByMinute = new ArrayList<>();
+ statusList = new ArrayList<>();
}
@Override
@@ -36,19 +35,19 @@ public class ActuatorMetricService implements IActuatorMetricService {
final int rowCount = statusMetricsByMinute.size() + 1;
final Object[][] result = new Object[rowCount][colCount];
result[0][0] = "Time";
- int j = 1;
+ int j = 1;
for (final String status : statusList) {
result[0][j] = status;
j++;
}
for (int i = 1; i < rowCount; i++) {
- result[i][0] = dateFormat.format(new Date(current.getTime() - (60000 * (rowCount - i))));
+ result[i][0] = DATE_FORMAT.format(new Date(current.getTime() - (60000L * (rowCount - i))));
}
List minuteOfStatuses;
- List last = new ArrayList();
+ List last = new ArrayList<>();
for (int i = 1; i < rowCount; i++) {
minuteOfStatuses = statusMetricsByMinute.get(i - 1);
@@ -64,11 +63,9 @@ public class ActuatorMetricService implements IActuatorMetricService {
return result;
}
- // Non - API
-
- @Scheduled(fixedDelay = 60000)
+ @Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds:60000}")
private void exportMetrics() {
- final ArrayList lastMinuteStatuses = initializeStatuses(statusList.size());
+ final List lastMinuteStatuses = initializeStatuses(statusList.size());
for (final Meter counterMetric : publicMetrics.getMeters()) {
updateMetrics(counterMetric, lastMinuteStatuses);
@@ -77,34 +74,32 @@ public class ActuatorMetricService implements IActuatorMetricService {
statusMetricsByMinute.add(lastMinuteStatuses);
}
- private ArrayList initializeStatuses(final int size) {
- final ArrayList counterList = new ArrayList();
+ private List initializeStatuses(int size) {
+ List counterList = new ArrayList<>();
for (int i = 0; i < size; i++) {
counterList.add(0);
}
return counterList;
}
- private void updateMetrics(final Meter counterMetric, final ArrayList statusCount) {
- String status = "";
- int index = -1;
- int oldCount = 0;
+ private void updateMetrics(Meter counterMetric, List statusCount) {
- if (counterMetric.getId().getName().contains("counter.status.")) {
- status = counterMetric.getId().getName().substring(15, 18); // example 404, 200
+ String metricName = counterMetric.getId().getName();
+
+ if (metricName.contains("counter.status.")) {
+ // example 404, 200
+ String status = metricName.substring(15, 18);
appendStatusIfNotExist(status, statusCount);
- index = statusList.indexOf(status);
- oldCount = statusCount.get(index) == null ? 0 : statusCount.get(index);
+ int index = statusList.indexOf(status);
+ int oldCount = statusCount.get(index) == null ? 0 : statusCount.get(index);
statusCount.set(index, (int)((Counter) counterMetric).count() + oldCount);
}
}
- private void appendStatusIfNotExist(final String status, final ArrayList statusCount) {
+ private void appendStatusIfNotExist(String status, List statusCount) {
if (!statusList.contains(status)) {
statusList.add(status);
statusCount.add(0);
}
}
-
- //
-}
\ No newline at end of file
+}
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/CustomActuatorMetricService.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/CustomActuatorMetricService.java
similarity index 68%
rename from spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/CustomActuatorMetricService.java
rename to spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/CustomActuatorMetricService.java
index ee17825b7c..9b4ccaa875 100644
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/CustomActuatorMetricService.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/CustomActuatorMetricService.java
@@ -1,40 +1,36 @@
-package com.baeldung.web.metric;
+package com.baeldung.metrics.service;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.search.Search;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Service;
-
-import io.micrometer.core.instrument.Counter;
-import io.micrometer.core.instrument.MeterRegistry;
-import io.micrometer.core.instrument.search.Search;
-
@Service
-public class CustomActuatorMetricService implements ICustomActuatorMetricService {
+public class CustomActuatorMetricService implements MetricService {
+
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
@Autowired
private MeterRegistry registry;
- private final List> statusMetricsByMinute;
+ private final List> statusMetricsByMinute;
private final List statusList;
- private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
public CustomActuatorMetricService() {
- super();
- statusMetricsByMinute = new ArrayList>();
- statusList = new ArrayList();
+ statusMetricsByMinute = new ArrayList<>();
+ statusList = new ArrayList<>();
}
- // API
-
- @Override
- public void increaseCount(final int status) {
+ public void increaseCount(int status) {
String counterName = "counter.status." + status;
- registry.counter(counterName).increment(1);
+ registry.counter(counterName).increment();
if (!statusList.contains(counterName)) {
statusList.add(counterName);
}
@@ -55,7 +51,7 @@ public class CustomActuatorMetricService implements ICustomActuatorMetricService
}
for (int i = 1; i < rowCount; i++) {
- result[i][0] = dateFormat.format(new Date(current.getTime() - (60000 * (rowCount - i))));
+ result[i][0] = DATE_FORMAT.format(new Date(current.getTime() - (60000L * (rowCount - i))));
}
List minuteOfStatuses;
@@ -72,19 +68,17 @@ public class CustomActuatorMetricService implements ICustomActuatorMetricService
return result;
}
- // Non - API
-
- @Scheduled(fixedDelay = 60000)
+ @Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds:60000}")
private void exportMetrics() {
- final ArrayList statusCount = new ArrayList();
+ List statusCount = new ArrayList<>();
for (final String status : statusList) {
Search search = registry.find(status);
- if (search != null) {
- Counter counter = search.counter();
- statusCount.add(counter != null ? ((int) counter.count()) : 0);
- registry.remove(counter);
- } else {
+ Counter counter = search.counter();
+ if (counter == null) {
statusCount.add(0);
+ } else {
+ statusCount.add((int) counter.count());
+ registry.remove(counter);
}
}
statusMetricsByMinute.add(statusCount);
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/InMemoryMetricService.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/InMemoryMetricService.java
new file mode 100644
index 0000000000..0be5c21727
--- /dev/null
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/InMemoryMetricService.java
@@ -0,0 +1,112 @@
+package com.baeldung.metrics.service;
+
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Service
+public class InMemoryMetricService implements MetricService {
+
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+
+ private final Map> metricMap;
+ private final Map statusMetric;
+ private final Map> timeMap;
+
+ public InMemoryMetricService() {
+ metricMap = new ConcurrentHashMap<>();
+ statusMetric = new ConcurrentHashMap<>();
+ timeMap = new ConcurrentHashMap<>();
+ }
+
+ public void increaseCount(String request, int status) {
+ increaseMainMetric(request, status);
+ increaseStatusMetric(status);
+ updateTimeMap(status);
+ }
+
+ public Map> getFullMetric() {
+ return metricMap;
+ }
+
+ public Map getStatusMetric() {
+ return statusMetric;
+ }
+
+ public Object[][] getGraphData() {
+ final int colCount = statusMetric.keySet().size() + 1;
+ final Set allStatus = statusMetric.keySet();
+ final int rowCount = timeMap.keySet().size() + 1;
+
+ final Object[][] result = new Object[rowCount][colCount];
+ result[0][0] = "Time";
+
+ int j = 1;
+ for (final int status : allStatus) {
+ result[0][j] = status;
+ j++;
+ }
+ int i = 1;
+ Map tempMap;
+ for (final Entry> entry : timeMap.entrySet()) {
+ result[i][0] = entry.getKey();
+ tempMap = entry.getValue();
+ for (j = 1; j < colCount; j++) {
+ result[i][j] = tempMap.get((Integer) result[0][j]);
+ if (result[i][j] == null) {
+ result[i][j] = 0;
+ }
+ }
+ i++;
+ }
+
+ for (int k = 1; k < result[0].length; k++) {
+ result[0][k] = result[0][k].toString();
+ }
+
+ return result;
+ }
+
+ private void increaseMainMetric(String request, int status) {
+ Map statusMap = metricMap.get(request);
+ if (statusMap == null) {
+ statusMap = new ConcurrentHashMap<>();
+ }
+
+ Integer count = statusMap.get(status);
+ if (count == null) {
+ count = 1;
+ } else {
+ count++;
+ }
+ statusMap.put(status, count);
+ metricMap.put(request, statusMap);
+ }
+
+ private void increaseStatusMetric(int status) {
+ statusMetric.merge(status, 1, Integer::sum);
+ }
+
+ private void updateTimeMap(int status) {
+ final String time = DATE_FORMAT.format(new Date());
+ Map statusMap = timeMap.get(time);
+ if (statusMap == null) {
+ statusMap = new ConcurrentHashMap<>();
+ }
+
+ Integer count = statusMap.get(status);
+ if (count == null) {
+ count = 1;
+ } else {
+ count++;
+ }
+ statusMap.put(status, count);
+ timeMap.put(time, statusMap);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/MetricService.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/MetricService.java
new file mode 100644
index 0000000000..3642102b67
--- /dev/null
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/service/MetricService.java
@@ -0,0 +1,7 @@
+package com.baeldung.metrics.service;
+
+public interface MetricService {
+
+ Object[][] getGraphData();
+
+}
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/resources/application-metrics.properties b/spring-boot-modules/spring-boot-actuator/src/main/resources/application-metrics.properties
new file mode 100644
index 0000000000..5f753a0c62
--- /dev/null
+++ b/spring-boot-modules/spring-boot-actuator/src/main/resources/application-metrics.properties
@@ -0,0 +1,9 @@
+management.endpoints.web.exposure.include=info,health,metrics
+
+# JPA and Security is not required for Metrics application
+spring.autoconfigure.exclude= org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, \
+ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, \
+ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, \
+ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, \
+ org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration, \
+ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration
diff --git a/spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/api-servlet.xml b/spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/api-servlet.xml
similarity index 100%
rename from spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/api-servlet.xml
rename to spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/api-servlet.xml
diff --git a/spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/view/graph.jsp b/spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/view/metrics/graph.jsp
similarity index 94%
rename from spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/view/graph.jsp
rename to spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/view/metrics/graph.jsp
index e1d5fdc987..75976557a0 100644
--- a/spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/view/graph.jsp
+++ b/spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/view/metrics/graph.jsp
@@ -11,7 +11,7 @@
});
function drawChart() {
- $.get("",
+ $.get("",
function(mydata) {
var data = google.visualization.arrayToDataTable(mydata);
diff --git a/spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/view/homepage.jsp b/spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/view/metrics/homepage.jsp
similarity index 100%
rename from spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/view/homepage.jsp
rename to spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/view/metrics/homepage.jsp
diff --git a/spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/web.xml b/spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/web.xml
similarity index 92%
rename from spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/web.xml
rename to spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/web.xml
index 7129b6b4af..84566ba93d 100644
--- a/spring-web-modules/spring-rest-testing/src/main/webapp/WEB-INF/web.xml
+++ b/spring-boot-modules/spring-boot-actuator/src/main/webapp/WEB-INF/web.xml
@@ -16,7 +16,7 @@
contextConfigLocation
- com.baeldung.spring
+ com.baeldung.metrics
@@ -37,7 +37,7 @@
metricFilter
- com.baeldung.web.metric.MetricFilter
+ com.baeldung.metrics.filter.MetricFilter
diff --git a/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/metrics/MetricsApplicationIntegrationTest.java b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/metrics/MetricsApplicationIntegrationTest.java
new file mode 100644
index 0000000000..c83d4625dc
--- /dev/null
+++ b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/metrics/MetricsApplicationIntegrationTest.java
@@ -0,0 +1,64 @@
+package com.baeldung.metrics;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@SpringBootTest(
+ classes = MetricsApplication.class,
+ webEnvironment = RANDOM_PORT,
+ properties = {"fixedDelay.in.milliseconds=2000"}
+)
+@ActiveProfiles("metrics")
+class MetricsApplicationIntegrationTest {
+
+ @Autowired
+ private TestRestTemplate restTemplate;
+
+ @Test
+ void givenStatuses_WhenScheduledMethodExecuted_ExpectCountsAreAggregated() {
+ restTemplate.getForObject("/metrics/metric/notFound", String.class);
+ restTemplate.getForObject("/metrics/metric", String.class);
+
+ await().untilAsserted(() -> {
+ Object[][] statusCounts = restTemplate.getForObject("/metrics/metric-graph-data", Object[][].class);
+
+ assertThat(statusCounts[0]).contains("counter.status.200", "counter.status.404");
+
+ List requestCounts = getRequestCounts(statusCounts);
+ verify404RequestFrom(requestCounts);
+ verify200RequestsFrom(requestCounts);
+ });
+ }
+
+ private static void verify200RequestsFrom(List requestCounts) {
+ assertThat(requestCounts.size()).isGreaterThan(1);
+ }
+
+ private static void verify404RequestFrom(List requestCounts) {
+ assertThat(requestCounts).contains(1);
+ }
+
+ private static List getRequestCounts(Object[][] statusCounts) {
+ List requestCounts = new ArrayList<>();
+ for (int i = 1; i < statusCounts.length; i++) {
+ for (int j = 1; j < statusCounts[i].length; j++) {
+ Integer count = (Integer) statusCounts[i][j];
+ if (count >= 1) {
+ requestCounts.add(count);
+ }
+ }
+ }
+ return requestCounts;
+ }
+
+}
diff --git a/spring-web-modules/spring-rest-testing/README.md b/spring-web-modules/spring-rest-testing/README.md
index 2a77b5dded..806e67b7ec 100644
--- a/spring-web-modules/spring-rest-testing/README.md
+++ b/spring-web-modules/spring-rest-testing/README.md
@@ -11,7 +11,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
### Relevant Articles:
- [Integration Testing with the Maven Cargo plugin](https://www.baeldung.com/integration-testing-with-the-maven-cargo-plugin)
-- [Metrics for your Spring REST API](https://www.baeldung.com/spring-rest-api-metrics)
- [Testing Exceptions with Spring MockMvc](https://www.baeldung.com/spring-mvc-test-exceptions)
### Build the Project
diff --git a/spring-web-modules/spring-rest-testing/pom.xml b/spring-web-modules/spring-rest-testing/pom.xml
index 52c1f6418e..1390898bf9 100644
--- a/spring-web-modules/spring-rest-testing/pom.xml
+++ b/spring-web-modules/spring-rest-testing/pom.xml
@@ -21,19 +21,10 @@
org.springframework.boot
spring-boot-starter-web
-
- org.springframework.boot
- spring-boot-starter-actuator
-
org.aspectj
aspectjweaver
-
- org.apache.tomcat.embed
- tomcat-embed-jasper
- provided
-
org.springframework
@@ -69,14 +60,6 @@
org.springframework
spring-expression
-
- org.springframework
- spring-web
-
-
- org.springframework
- spring-webmvc
-
org.springframework.data
spring-data-commons
@@ -137,17 +120,6 @@
net.bytebuddy
byte-buddy
-
-
- javax.servlet
- javax.servlet-api
- provided
-
-
- javax.servlet
- jstl
- runtime
-
com.fasterxml.jackson.core
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/spring/Application.java b/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/spring/Application.java
index c35c4d7e5e..78c9b88ddb 100644
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/spring/Application.java
+++ b/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/spring/Application.java
@@ -1,38 +1,19 @@
package com.baeldung.spring;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.builder.SpringApplicationBuilder;
-import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import org.springframework.web.context.request.RequestContextListener;
/**
* Main Application Class - uses Spring Boot. Just run this as a normal Java
* class to run up a Jetty Server (on http://localhost:8082/spring-rest-full)
*
*/
-@EnableScheduling
@EnableAutoConfiguration
@ComponentScan("com.baeldung")
@SpringBootApplication
-public class Application extends SpringBootServletInitializer {
-
- @Override
- protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
- return application.sources(Application.class);
- }
-
- @Override
- public void onStartup(ServletContext sc) throws ServletException {
- // Manages the lifecycle of the root application context
- sc.addListener(new RequestContextListener());
- }
+public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/controller/RootController.java b/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/controller/RootController.java
deleted file mode 100644
index 005f6f023b..0000000000
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/controller/RootController.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.baeldung.web.controller;
-
-import java.util.Map;
-
-import com.baeldung.web.metric.IActuatorMetricService;
-import com.baeldung.web.metric.IMetricService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-@Controller
-@RequestMapping(value = "/auth/")
-public class RootController {
-
- @Autowired
- private IMetricService metricService;
-
- @Autowired
- private IActuatorMetricService actMetricService;
-
- public RootController() {
- super();
- }
-
- // API
-
- @RequestMapping(value = "/metric", method = RequestMethod.GET)
- @ResponseBody
- public Map getMetric() {
- return metricService.getFullMetric();
- }
-
- @RequestMapping(value = "/status-metric", method = RequestMethod.GET)
- @ResponseBody
- public Map getStatusMetric() {
- return metricService.getStatusMetric();
- }
-
- @RequestMapping(value = "/metric-graph", method = RequestMethod.GET)
- @ResponseBody
- public Object[][] drawMetric() {
- final Object[][] result = metricService.getGraphData();
- for (int i = 1; i < result[0].length; i++) {
- result[0][i] = result[0][i].toString();
- }
- return result;
- }
-
-
-}
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/IActuatorMetricService.java b/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/IActuatorMetricService.java
deleted file mode 100644
index 60bb43ee00..0000000000
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/IActuatorMetricService.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.baeldung.web.metric;
-
-public interface IActuatorMetricService {
- Object[][] getGraphData();
-}
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/ICustomActuatorMetricService.java b/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/ICustomActuatorMetricService.java
deleted file mode 100644
index 5126252e27..0000000000
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/ICustomActuatorMetricService.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.baeldung.web.metric;
-
-public interface ICustomActuatorMetricService {
-
- void increaseCount(final int status);
-
- Object[][] getGraphData();
-}
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/IMetricService.java b/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/IMetricService.java
deleted file mode 100644
index b8dfa60215..0000000000
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/IMetricService.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.baeldung.web.metric;
-
-import java.util.Map;
-
-public interface IMetricService {
-
- void increaseCount(final String request, final int status);
-
- Map getFullMetric();
-
- Map getStatusMetric();
-
- Object[][] getGraphData();
-}
diff --git a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/MetricService.java b/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/MetricService.java
deleted file mode 100644
index fd3cccab3e..0000000000
--- a/spring-web-modules/spring-rest-testing/src/main/java/com/baeldung/web/metric/MetricService.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package com.baeldung.web.metric;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.springframework.stereotype.Service;
-
-@Service
-public class MetricService implements IMetricService {
-
- private ConcurrentMap> metricMap;
- private ConcurrentMap statusMetric;
- private ConcurrentMap> timeMap;
- private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-
- public MetricService() {
- super();
- metricMap = new ConcurrentHashMap>();
- statusMetric = new ConcurrentHashMap();
- timeMap = new ConcurrentHashMap>();
- }
-
- // API
-
- @Override
- public void increaseCount(final String request, final int status) {
- increaseMainMetric(request, status);
- increaseStatusMetric(status);
- updateTimeMap(status);
- }
-
- @Override
- public Map getFullMetric() {
- return metricMap;
- }
-
- @Override
- public Map getStatusMetric() {
- return statusMetric;
- }
-
- @Override
- public Object[][] getGraphData() {
- final int colCount = statusMetric.keySet().size() + 1;
- final Set allStatus = statusMetric.keySet();
- final int rowCount = timeMap.keySet().size() + 1;
-
- final Object[][] result = new Object[rowCount][colCount];
- result[0][0] = "Time";
-
- int j = 1;
- for (final int status : allStatus) {
- result[0][j] = status;
- j++;
- }
- int i = 1;
- ConcurrentMap tempMap;
- for (final Entry> entry : timeMap.entrySet()) {
- result[i][0] = entry.getKey();
- tempMap = entry.getValue();
- for (j = 1; j < colCount; j++) {
- result[i][j] = tempMap.get(result[0][j]);
- if (result[i][j] == null) {
- result[i][j] = 0;
- }
- }
- i++;
- }
-
- return result;
- }
-
- // NON-API
-
- private void increaseMainMetric(final String request, final int status) {
- ConcurrentHashMap statusMap = metricMap.get(request);
- if (statusMap == null) {
- statusMap = new ConcurrentHashMap();
- }
-
- Integer count = statusMap.get(status);
- if (count == null) {
- count = 1;
- } else {
- count++;
- }
- statusMap.put(status, count);
- metricMap.put(request, statusMap);
- }
-
- private void increaseStatusMetric(final int status) {
- final Integer statusCount = statusMetric.get(status);
- if (statusCount == null) {
- statusMetric.put(status, 1);
- } else {
- statusMetric.put(status, statusCount + 1);
- }
- }
-
- private void updateTimeMap(final int status) {
- final String time = dateFormat.format(new Date());
- ConcurrentHashMap statusMap = timeMap.get(time);
- if (statusMap == null) {
- statusMap = new ConcurrentHashMap();
- }
-
- Integer count = statusMap.get(status);
- if (count == null) {
- count = 1;
- } else {
- count++;
- }
- statusMap.put(status, count);
- timeMap.put(time, statusMap);
- }
-
-}
diff --git a/spring-web-modules/spring-rest-testing/src/main/resources/application.properties b/spring-web-modules/spring-rest-testing/src/main/resources/application.properties
index 52d93b4cff..cb9d72e41f 100644
--- a/spring-web-modules/spring-rest-testing/src/main/resources/application.properties
+++ b/spring-web-modules/spring-rest-testing/src/main/resources/application.properties
@@ -1,3 +1,2 @@
server.port=8082
server.servlet.context-path=/spring-rest-full
-endpoints.metrics.enabled=true
\ No newline at end of file