Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.vaadin</groupId>
<artifactId>observability-kit-micrometer-test</artifactId>
<version>5.0-SNAPSHOT</version>
</parent>
<artifactId>observability-kit-spring-tests</artifactId>

<packaging>war</packaging>
<name>Test Vaadin Observability Kit plain-Spring integration on a real Jetty deployment</name>

<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>observability-kit-spring</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-testbench-core-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-server</artifactId>
<version>${flow.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-client</artifactId>
<version>${flow.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-html-components</artifactId>
<version>${flow.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${servlet.api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-html-components-testbench</artifactId>
<version>${flow.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<defaultGoal>jetty:run</defaultGoal>
<plugins>
<!-- Run flow plugin to build frontend -->
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>flow-maven-plugin</artifactId>
<version>${flow.version}</version>
<executions>
<execution>
<goals>
<goal>prepare-frontend</goal>
<goal>build-frontend</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
<!-- Run jetty before integration tests, and stop after -->
<plugin>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-maven-plugin</artifactId>
<version>12.1.8</version>

<configuration>
<!-- Control port/key the stop goal uses to talk to the running
server; without these the stop-jetty execution fails with
"Please specify a valid port". -->
<stopPort>8089</stopPort>
<stopKey>observability-kit-spring</stopKey>
<stopWait>5</stopWait>
</configuration>

<executions>
<!-- start and stop jetty (running our app) when running
integration tests -->
<execution>
<id>start-jetty</id>
<goals>
<goal>start</goal>
</goals>
<phase>pre-integration-test</phase>
</execution>
<execution>
<id>stop-jetty</id>
<goals>
<goal>stop</goal>
</goals>
<phase>post-integration-test</phase>
</execution>
</executions>
</plugin>
<!-- Run the *IT browser tests against the running Jetty. The tests are
JUnit 5 TestBench tests (@BrowserTest), so failsafe uses the JUnit
Platform provider (active via junit-jupiter on the test classpath).
Unlike flow-tests we do not force the surefire-junit47 provider,
which only runs JUnit 4 tests and would skip these. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Plain Spring project: assert no Spring Boot reaches the classpath -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.6.3</version>
<executions>
<execution>
<id>enforce-banned-dependencies</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>org.springframework.boot</exclude>
</excludes>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<!--
This file is auto-generated by Vaadin.
-->

<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<style>
html, body, #outlet {
height: 100%;
width: 100%;
margin: 0;
}
</style>
<!-- index.ts is included here automatically (either by the dev server or during the build) -->
</head>
<body>
<!-- This outlet div is where the views are rendered -->
<div id="outlet"></div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (C) 2000-2026 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See <https://vaadin.com/commercial-license-and-service-terms> for the full
* license.
*/
package com.vaadin.observability.spring.tests;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.vaadin.observability.spring.ObservabilityConfiguration;

/**
* Plain Spring (non-Boot) configuration importing the observability-kit Spring
* wiring and providing a {@link MeterRegistry}. Component-scans the test
* package so views and the metrics servlet are picked up.
*/
@Configuration
@ComponentScan
@Import(ObservabilityConfiguration.class)
public class AppConfiguration {

@Bean
public MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (C) 2000-2026 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See <https://vaadin.com/commercial-license-and-service-terms> for the full
* license.
*/
package com.vaadin.observability.spring.tests;

import java.util.Collection;
import java.util.Collections;

import com.vaadin.flow.spring.VaadinMVCWebAppInitializer;

/**
* Bootstraps the Spring MVC context with {@link AppConfiguration}.
*/
public class AppWebAppInitializer extends VaadinMVCWebAppInitializer {

@Override
protected Collection<Class<?>> getConfigurationClasses() {
return Collections.singletonList(AppConfiguration.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (C) 2000-2026 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See <https://vaadin.com/commercial-license-and-service-terms> for the full
* license.
*/
package com.vaadin.observability.spring.tests;

import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.router.Route;

/**
* Landing view; navigating to it drives session/UI/navigation/request lifecycle
* through Flow so the micrometer binders fire.
*/
@Route("")
public class HelloView extends Div {

public HelloView() {
Span greeting = new Span("Hello micrometer spring");
greeting.setId("greeting");
add(greeting);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright (C) 2000-2026 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See <https://vaadin.com/commercial-license-and-service-terms> for the full
* license.
*/
package com.vaadin.observability.spring.tests;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
* Dumps the Spring-managed {@link MeterRegistry} as deterministic text the IT
* scrapes via HTTP.
*/
@WebServlet(urlPatterns = "/metrics", asyncSupported = false)
public class MetricsServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
WebApplicationContext ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(getServletContext());
MeterRegistry registry = ctx.getBean(MeterRegistry.class);
resp.setContentType("text/plain; charset=utf-8");
try (PrintWriter writer = resp.getWriter()) {
registry.getMeters().stream()
.sorted((a, b) -> a.getId().getName()
.compareTo(b.getId().getName()))
.forEach(meter -> writeMeter(writer, meter));
}
}

private void writeMeter(PrintWriter writer, Meter meter) {
String id = formatId(meter);
if (meter instanceof Counter c) {
writer.printf("%s count=%.0f%n", id, c.count());
} else if (meter instanceof Gauge g) {
writer.printf("%s value=%.0f%n", id, g.value());
} else if (meter instanceof Timer t) {
writer.printf("%s count=%d total_ms=%.3f%n", id, t.count(),
t.totalTime(TimeUnit.MILLISECONDS));
}
}

private String formatId(Meter meter) {
StringBuilder sb = new StringBuilder(meter.getId().getName());
for (Tag tag : meter.getId().getTagsAsIterable()) {
sb.append(' ').append(tag.getKey()).append('=')
.append(tag.getValue());
}
return sb.toString();
}
}
Loading
Loading