Skip to main content

Servlet Sample

The Servlet sample demonstrates a complete Spring Boot Admin Server deployment using traditional servlet-based Spring MVC. This is the most feature-rich sample, showcasing security, custom UI extensions, notifications, and self-monitoring.

Overview

Location: spring-boot-admin-samples/spring-boot-admin-sample-servlet/

Features:

  • Traditional servlet-based deployment (Spring MVC)
  • Spring Security integration with form login
  • Self-monitoring using Admin Client
  • Mail notifications configured
  • Custom UI extensions included
  • Custom notifier implementation
  • HTTP exchange tracking
  • Audit event logging
  • Session persistence with JDBC
  • Custom actuator endpoint
  • JMX support via Jolokia

Prerequisites

  • Java 17 or higher
  • Maven 3.6+
  • Optional: Mail server for notifications

Running the Sample

Quick Start

cd spring-boot-admin-samples/spring-boot-admin-sample-servlet
mvn spring-boot:run

Access the application at: http://localhost:8080

With Security Enabled

mvn spring-boot:run -Dspring-boot.run.profiles=secure

Login Credentials:

  • Username: user
  • Password: password

Change Port

SERVER_PORT=9090 mvn spring-boot:run

Project Structure

Dependencies

Key dependencies from pom.xml:

<dependencies>
<!-- Admin Server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

<!-- Admin Client (for self-monitoring) -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

<!-- Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- Web (Servlet) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>

<!-- Notifications -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<!-- Session Management -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
</dependency>

<!-- Custom UI Extensions -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-sample-custom-ui</artifactId>
</dependency>
</dependencies>

Main Application Class

SpringBootAdminServletApplication.java
@SpringBootApplication
@EnableAdminServer
@EnableCaching
public class SpringBootAdminServletApplication {

static void main(String[] args) {
SpringApplication app = new SpringApplication(
SpringBootAdminServletApplication.class
);
app.setApplicationStartup(new BufferingApplicationStartup(1500));
app.run(args);
}

@Bean
public CustomNotifier customNotifier(InstanceRepository repository) {
return new CustomNotifier(repository);
}

@Bean
public HttpHeadersProvider customHttpHeadersProvider() {
return (instance) -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("X-CUSTOM", "My Custom Value");
return httpHeaders;
};
}

@Bean
public InstanceExchangeFilterFunction auditLog() {
return (instance, request, next) -> next.exchange(request)
.doOnSubscribe((s) -> {
if (HttpMethod.DELETE.equals(request.method())
|| HttpMethod.POST.equals(request.method())) {
log.info("{} for {} on {}",
request.method(), instance.getId(), request.url());
}
});
}
}

Key Points:

  • @EnableAdminServer activates Admin Server functionality
  • Custom HTTP headers added to all instance requests
  • Audit logging for DELETE/POST operations
  • Application startup tracking enabled

Configuration

Application Configuration

application.yml
spring:
application:
name: spring-boot-admin-sample-servlet

boot:
admin:
client:
url: http://localhost:8080 # Self-registration
instance:
service-host-type: IP
metadata:
tags:
environment: test

management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
shutdown:
enabled: true
restart:
enabled: true

logging:
file:
name: "target/boot-admin-sample-servlet.log"
level:
de.codecentric: info

Static Instance Configuration

The sample includes multiple static instances with creative names:

spring:
cloud:
discovery:
client:
simple:
instances:
"Captain Debugbeard":
- uri: http://localhost:8080
metadata:
management.context-path: /actuator
group: Pirates of the Caribbean Bean

"Stack Overflow Sorcerer":
- uri: http://localhost:8080
metadata:
management.context-path: /actuator
group: Wizarding World

Security Configuration

Spring Security Setup

SecuritySecureConfig.java
@Profile("secure")
@Configuration
public class SecuritySecureConfig {

private final AdminServerProperties adminServer;

@Bean
protected SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(adminServer.path("/"));

http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests
.requestMatchers(adminServer.path("/assets/**"))
.permitAll() // Allow static resources
.requestMatchers(adminServer.path("/actuator/info"))
.permitAll()
.requestMatchers(adminServer.path("/actuator/health"))
.permitAll()
.requestMatchers(adminServer.path("/login"))
.permitAll()
.anyRequest()
.authenticated())
.formLogin((formLogin) -> formLogin
.loginPage(adminServer.path("/login"))
.successHandler(successHandler))
.logout((logout) -> logout
.logoutUrl(adminServer.path("/logout")))
.httpBasic(Customizer.withDefaults());

// CSRF Configuration
http.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
adminServer.path("/instances"), // Instance registration
adminServer.path("/instances/*"), // Instance deregistration
adminServer.path("/actuator/**") // Actuator endpoints
));

http.rememberMe((rememberMe) -> rememberMe
.key(UUID.randomUUID().toString())
.tokenValiditySeconds(1209600)); // 14 days

return http.build();
}

@Bean
public InMemoryUserDetailsManager userDetailsService(
PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Security Features:

  1. Form-based login with custom page
  2. HTTP Basic authentication support
  3. CSRF protection with token repository
  4. Remember-me functionality (14 days)
  5. Static resources publicly accessible
  6. Health/info endpoints publicly accessible

Custom Notifier

The sample includes a custom notifier implementation:

CustomNotifier.java
public class CustomNotifier extends AbstractEventNotifier {

private static final Logger LOGGER =
LoggerFactory.getLogger(CustomNotifier.class);

public CustomNotifier(InstanceRepository repository) {
super(repository);
}

@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
if (event instanceof InstanceStatusChangedEvent statusChangedEvent) {
LOGGER.info("Instance {} ({}) is {}",
instance.getRegistration().getName(),
event.getInstance(),
statusChangedEvent.getStatusInfo().getStatus());
} else {
LOGGER.info("Instance {} ({}) {}",
instance.getRegistration().getName(),
event.getInstance(),
event.getType());
}
});
}
}

Notifier Configuration

NotifierConfig.java
@Configuration
public class NotifierConfig {

@Bean
public FilteringNotifier filteringNotifier() {
CompositeNotifier delegate = new CompositeNotifier(
otherNotifiers.getIfAvailable(Collections::emptyList)
);
return new FilteringNotifier(delegate, repository);
}

@Primary
@Bean(initMethod = "start", destroyMethod = "stop")
public RemindingNotifier remindingNotifier() {
RemindingNotifier notifier = new RemindingNotifier(
filteringNotifier(), repository
);
notifier.setReminderPeriod(Duration.ofMinutes(10));
notifier.setCheckReminderInverval(Duration.ofSeconds(10));
return notifier;
}
}

Notification Features:

  • Custom event logging
  • Filtering notifier for selective notifications
  • Reminding notifier (sends reminders every 10 minutes)
  • Composable notifier chain

UI Customizations

External Views

The sample demonstrates various external view configurations:

spring:
boot:
admin:
ui:
external-views:
# Simple link
- label: "🚀"
url: "https://codecentric.de"
order: 2000

# Dropdown with links
- label: Resources
children:
- label: "📖 Docs"
url: https://codecentric.github.io/spring-boot-admin/
- label: "📦 Maven"
url: https://search.maven.org/...
- label: "🐙 GitHub"
url: https://github.com/codecentric/spring-boot-admin

# Iframe view
- label: "🎅 Is it christmas"
url: https://isitchristmas.com
iframe: true

View Settings

spring:
boot:
admin:
ui:
view-settings:
- name: "journal"
enabled: false

Session Management

The sample uses JDBC-based session persistence:

@Bean
public EmbeddedDatabase dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("org/springframework/session/jdbc/schema-hsqldb.sql")
.build();
}

Benefits:

  • Session persistence across restarts
  • Support for clustered deployments
  • Built-in session cleanup

Testing the Sample

Access the UI

  1. Start the application
  2. Navigate to http://localhost:8080
  3. Login (if secure profile is active)
  4. View the registered instances

Test Self-Monitoring

The application monitors itself via the Admin Client. You should see:

  • Application name: spring-boot-admin-sample-servlet
  • Status: UP
  • All actuator endpoints available
  • Custom metadata and tags

Test Notifications

Monitor the logs for custom notification events:

INFO - Instance spring-boot-admin-sample-servlet (...) is UP
INFO - Instance spring-boot-admin-sample-servlet (...) ENDPOINTS_DETECTED

Test Custom Headers

All requests to instances include the custom header X-CUSTOM: My Custom Value.

Test External Views

Click the external view links in the navigation:

  • Rocket emoji (🚀) - Opens codecentric.de
  • Resources dropdown - Multiple documentation links
  • "Is it christmas" - Iframe view

Build and Deploy

Build JAR

mvn clean package

Produces: target/spring-boot-admin-sample-servlet.jar

Run JAR

java -jar target/spring-boot-admin-sample-servlet.jar

With Profiles

java -jar target/spring-boot-admin-sample-servlet.jar \
--spring.profiles.active=secure

Deployment Considerations

When deploying, consider:

  1. External Database: Replace HSQLDB with PostgreSQL/MySQL
  2. Mail Server: Configure SMTP for real notifications
  3. Security: Use external user store (LDAP/OAuth2)
  4. HTTPS: Enable TLS/SSL
  5. Session Store: Use Redis or external database

Example deployment configuration:

spring:
datasource:
url: jdbc:postgresql://localhost:5432/admin
username: admin
password: ${DB_PASSWORD}

mail:
host: smtp.company.com
port: 587
username: ${SMTP_USER}
password: ${SMTP_PASSWORD}

server:
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: ${KEYSTORE_PASSWORD}

Customization Ideas

Add Custom Actuator Endpoint

Create a custom endpoint (example included):

@Component
@Endpoint(id = "custom")
public class CustomEndpoint {

@ReadOperation
public Map<String, Object> customEndpoint() {
return Map.of(
"message", "Hello from custom endpoint",
"timestamp", Instant.now()
);
}
}

Add Database Notifier

Replace log-based notifier with database persistence:

public class DatabaseNotifier extends AbstractEventNotifier {

private final EventRepository eventRepository;

@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
eventRepository.save(new EventEntity(event, instance));
});
}
}

Add Custom Metadata

Enhance self-registration with custom metadata:

spring:
boot:
admin:
client:
instance:
metadata:
version: ${project.version}
region: us-east-1
team: platform
tags:
environment: production
cost-center: engineering

Troubleshooting

Port Already in Use

# Change port
SERVER_PORT=9090 mvn spring-boot:run

Security Issues

If you cannot access the UI:

  1. Check if secure profile is active
  2. Verify credentials: user / password
  3. Check CSRF token in browser console
  4. Clear browser cookies

Self-Registration Fails

  1. Verify client URL matches server URL
  2. Check actuator endpoints are exposed
  3. Review logs for connection errors
  4. Ensure security permits instance registration

Mail Notifications Not Working

  1. Configure valid SMTP server
  2. Check firewall/network access
  3. Verify credentials
  4. Enable debug logging:
logging:
level:
org.springframework.mail: DEBUG

Key Takeaways

This sample demonstrates:

Complete Deployment Setup

  • Security, session management, notifications

Self-Monitoring Pattern

  • Admin Server monitoring itself via Admin Client

Extensibility

  • Custom notifiers, endpoints, headers, UI views

Best Practices

  • Profile-based configuration
  • CSRF protection
  • Proper security configuration

Next Steps

See Also