Skip to content

Commit 272633e

Browse files
committed
Update to Brixton and add Eureka-Service converter
Update to Spring Cloud Brixton and add the concept of ServiceInstanceConverters. A ServiceConverter is used to convert discovered ServiceInstances to Applications. The old converter and one for Eureka is provided. It is also possible to provide your own implementation. closes codecentric#154
1 parent f8b9d82 commit 272633e

24 files changed

+545
-412
lines changed

pom.xml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<java.version>1.7</java.version>
1919
<main.basedir>${basedir}</main.basedir>
2020
<spring-boot.version>1.3.3.RELEASE</spring-boot.version>
21-
<spring-cloud.version>Angel.SR6</spring-cloud.version>
21+
<spring-cloud.version>Brixton.RC2</spring-cloud.version>
2222
<build-plugin.jacoco.version>0.7.5.201505241946</build-plugin.jacoco.version>
2323
<build-plugin.coveralls.version>4.0.0</build-plugin.coveralls.version>
2424
<build-plugin.gpg.version>1.6</build-plugin.gpg.version>
@@ -226,21 +226,21 @@
226226
</dependency>
227227
<dependency>
228228
<groupId>org.springframework.boot</groupId>
229-
<artifactId>spring-boot-starter-parent</artifactId>
229+
<artifactId>spring-boot-dependencies</artifactId>
230230
<version>${spring-boot.version}</version>
231231
<type>pom</type>
232232
<scope>import</scope>
233233
</dependency>
234234
<dependency>
235235
<groupId>org.springframework.cloud</groupId>
236-
<artifactId>spring-cloud-starter-parent</artifactId>
236+
<artifactId>spring-cloud-dependencies</artifactId>
237237
<version>${spring-cloud.version}</version>
238238
<type>pom</type>
239239
<scope>import</scope>
240240
</dependency>
241241
</dependencies>
242242
</dependencyManagement>
243-
<!-- Removed for release
243+
<!-- Remove for release -->
244244
<repositories>
245245
<repository>
246246
<id>spring-release</id>
@@ -256,6 +256,13 @@
256256
</snapshots>
257257
<url>http://repo.spring.io/milestone</url>
258258
</repository>
259+
<repository>
260+
<id>spring-snapshot</id>
261+
<snapshots>
262+
<enabled>true</enabled>
263+
</snapshots>
264+
<url>http://repo.spring.io/snapshot</url>
265+
</repository>
259266
</repositories>
260267
<pluginRepositories>
261268
<pluginRepository>
@@ -273,5 +280,5 @@
273280
<url>http://repo.spring.io/milestone</url>
274281
</pluginRepository>
275282
</pluginRepositories>
276-
-->
283+
<!-- -->
277284
</project>

spring-boot-admin-docs/src/main/asciidoc/index.adoc

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ The Spring Boot Admin Client registers the application at the admin server. This
191191

192192
| spring.boot.admin.client.enabled
193193
| Enables the Spring Boot Admin Client.
194-
| true
194+
| `true`
195195

196196
| spring.boot.admin.url
197197
| List of URLs of the Spring Boot Admin server to register at. This triggers the AutoConfiguration. *Mandatory*.
@@ -266,6 +266,14 @@ spring.boot.admin.password
266266
The Spring Boot Admin Server is capable of using Spring Clouds `DiscoveryClient` to discover applications. The advantage is that the clients don't have to include the `spring-boot-admin-starter-client`. You just have to add a DiscoveryClient to your admin server - everything else is done by AutoConfiguration.
267267
The setup is explained <<discover-clients-via-spring-cloud-discovery,above>>.
268268

269+
==== Usage of discovery informations ====
270+
271+
The informations of the discovered services is converted by the `ServiceInstanceConverter`. Spring Boot Admin ships with a default and Eureka converter implementation. The corresponding one is selected by AutoConfiguration. You can use your own conversion by simply implementing the interface and adding the bean to your application context.
272+
273+
TIP: When Eureka discovery is used, the `EurekaServiceInstanceConverter` will use the discovered instances homePageUrl and healthCheckUrl. In case the instances managment.context-path is different from the homePageUrl you should add an entry `management.context-path` to the instances metadata-map with the corresponding value.
274+
275+
TIP: When the default conversion kicks in, you can use the `spring.boot.admin.discovery.converter.*` properties to control the conversion for all your instances.
276+
269277
.Discovery configuration options
270278
|===
271279
| Property name |Description |Default value
@@ -274,12 +282,18 @@ The setup is explained <<discover-clients-via-spring-cloud-discovery,above>>.
274282
| Enables the DiscoveryClient-support for the admin server.
275283
| `true`
276284

277-
| spring.boot.admin.discovery.management.context-path
285+
| spring.boot.admin.discovery.management.context-path _(deprecated)_
278286
| If set this will be appended to the service-url from the discovery information.
279287
|
280-
|===
281288

282-
NOTE: Currently the implementation is not specific to any DiscoveryClient implementation. Unfortunately the DiscoveryClient doesn't expose the health- or management-url, so if you're not sticking to the defaults and setting `spring.boot.admin.discovery.management.context-path` doesn't suffice, you can add a bean extending `ApplicationDiscoveryListener` to your admin server and provide your own conversion.
289+
| spring.boot.admin.discovery.converter.management-context-path
290+
| Will be appended to the service-url of the discovered service when the managment-url is converter by the `DefaultServiceInstanceConverter`.
291+
|
292+
293+
| spring.boot.admin.discovery.converter.health-endpoint
294+
| Will be appended to the management-url of the discovered service when the health-url is converter by the `DefaultServiceInstanceConverter`.
295+
| `"health"`
296+
|===
283297

284298
[[hazelcast-support]]
285299
=== Hazelcast support ===
@@ -545,8 +559,5 @@ Can I include spring-boot-admin into my business application?::
545559
*tl;dr* You can, but you shouldn't. +
546560
You can set `spring.boot.admin.context-path` to alter the path where the UI and REST-API is served, but depending on the complexity of your application you might get in trouble. On the other hand in my opinion it makes no sense for an application to monitor itself. In case your application goes down your monitoring tool also does.
547561

548-
How do I disable Spring Boot Admin Client for my unit tests?::
549-
The AutoConfiguration is triggered by the presence of the `spring.boot.admin.url`. So if you don't set this property for your tests, the Spring Boot Admin Client is not active.
550-
551562
How do I customize the UI?::
552563
You can only customize the UI by copying and modifying the source of `spring-boot-admin-ui` and adding your own module to your classpath.

spring-boot-admin-server/pom.xml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
<dependency>
3030
<groupId>org.springframework.cloud</groupId>
3131
<artifactId>spring-cloud-netflix-core</artifactId>
32+
<!-- for fix of serverproeprties -->
33+
<version>1.1.0.BUILD-SNAPSHOT</version>
3234
</dependency>
3335
<dependency>
3436
<groupId>org.springframework.cloud</groupId>
@@ -38,18 +40,24 @@
3840
<groupId>org.apache.httpcomponents</groupId>
3941
<artifactId>httpclient</artifactId>
4042
</dependency>
41-
<!-- Optional Mail Starter for mail-notfications -->
43+
<!-- Optional Mail Starter for mail-notfications -->
4244
<dependency>
4345
<groupId>org.springframework.boot</groupId>
4446
<artifactId>spring-boot-starter-mail</artifactId>
45-
<optional>true</optional>
47+
<optional>true</optional>
4648
</dependency>
4749
<!-- Optional Discovery Client -->
4850
<dependency>
4951
<groupId>org.springframework.cloud</groupId>
5052
<artifactId>spring-cloud-starter</artifactId>
5153
<optional>true</optional>
5254
</dependency>
55+
<!-- Optional Eureka Discovery Client -->
56+
<dependency>
57+
<groupId>org.springframework.cloud</groupId>
58+
<artifactId>spring-cloud-starter-eureka</artifactId>
59+
<optional>true</optional>
60+
</dependency>
5361
<!-- Optional Hazelcast-Support -->
5462
<dependency>
5563
<groupId>com.hazelcast</groupId>

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/config/DiscoveryClientConfiguration.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,23 @@
1616
package de.codecentric.boot.admin.config;
1717

1818
import org.springframework.beans.factory.annotation.Autowired;
19-
import org.springframework.beans.factory.annotation.Value;
2019
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
20+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2122
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
25+
import org.springframework.boot.context.properties.ConfigurationProperties;
2426
import org.springframework.cloud.client.discovery.DiscoveryClient;
2527
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration;
28+
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient.EurekaServiceInstance;
2629
import org.springframework.context.annotation.Bean;
2730
import org.springframework.context.annotation.Configuration;
2831

2932
import de.codecentric.boot.admin.discovery.ApplicationDiscoveryListener;
33+
import de.codecentric.boot.admin.discovery.DefaultServiceInstanceConverter;
34+
import de.codecentric.boot.admin.discovery.EurekaServiceInstanceConverter;
35+
import de.codecentric.boot.admin.discovery.ServiceInstanceConverter;
3036
import de.codecentric.boot.admin.registry.ApplicationRegistry;
3137

3238
@Configuration
@@ -35,9 +41,6 @@
3541
@AutoConfigureAfter({ NoopDiscoveryClientAutoConfiguration.class })
3642
public class DiscoveryClientConfiguration {
3743

38-
@Value("${spring.boot.admin.discovery.management.context-path:}")
39-
private String managementPath;
40-
4144
@Autowired
4245
private DiscoveryClient discoveryClient;
4346

@@ -46,10 +49,32 @@ public class DiscoveryClientConfiguration {
4649

4750
@Bean
4851
@ConditionalOnMissingBean
49-
public ApplicationDiscoveryListener applicationDiscoveryListener() {
52+
public ApplicationDiscoveryListener applicationDiscoveryListener(
53+
ServiceInstanceConverter serviceInstanceConverter) {
5054
ApplicationDiscoveryListener listener = new ApplicationDiscoveryListener(discoveryClient,
5155
registry);
52-
listener.setManagementContextPath(managementPath);
56+
listener.setConverter(serviceInstanceConverter);
5357
return listener;
5458
}
59+
60+
@Configuration
61+
@ConditionalOnClass({ EurekaServiceInstance.class })
62+
@AutoConfigureBefore(DefaultConverterConfiguration.class)
63+
public static class EurekaConverterConfiguration {
64+
@Bean
65+
@ConditionalOnMissingBean
66+
public EurekaServiceInstanceConverter serviceInstanceConverter() {
67+
return new EurekaServiceInstanceConverter();
68+
}
69+
}
70+
71+
@Configuration
72+
@ConfigurationProperties(prefix = "spring.boot.admin.discovery.converter")
73+
public static class DefaultConverterConfiguration {
74+
@Bean
75+
@ConditionalOnMissingBean
76+
public DefaultServiceInstanceConverter serviceInstanceConverter() {
77+
return new DefaultServiceInstanceConverter();
78+
}
79+
}
5580
}

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/config/RevereseZuulProxyConfiguration.java

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@
1616
package de.codecentric.boot.admin.config;
1717

1818
import org.springframework.beans.factory.annotation.Autowired;
19+
import org.springframework.boot.actuate.endpoint.Endpoint;
1920
import org.springframework.boot.actuate.trace.TraceRepository;
2021
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2123
import org.springframework.boot.autoconfigure.web.ServerProperties;
24+
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
2225
import org.springframework.cloud.netflix.zuul.ZuulConfiguration;
2326
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
27+
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
28+
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
29+
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
2430
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
2531
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
2632
import org.springframework.context.ApplicationEvent;
@@ -32,7 +38,6 @@
3238
import de.codecentric.boot.admin.event.RoutesOutdatedEvent;
3339
import de.codecentric.boot.admin.registry.ApplicationRegistry;
3440
import de.codecentric.boot.admin.zuul.ApplicationRouteLocator;
35-
import de.codecentric.boot.admin.zuul.PreDecorationFilter;
3641

3742
@Configuration
3843
@AutoConfigureAfter({ AdminServerWebConfiguration.class })
@@ -50,16 +55,21 @@ public class RevereseZuulProxyConfiguration extends ZuulConfiguration {
5055
@Autowired
5156
private AdminServerProperties adminServer;
5257

58+
@Autowired
59+
private ZuulHandlerMapping zuulHandlerMapping;
60+
5361
@Bean
5462
@Override
5563
public ApplicationRouteLocator routeLocator() {
5664
return new ApplicationRouteLocator(this.server.getServletPrefix(), registry,
57-
adminServer.getContextPath() + "/api/applications");
65+
adminServer.getContextPath() + "/api/applications/");
5866
}
5967

68+
// pre filters
6069
@Bean
6170
public PreDecorationFilter preDecorationFilter() {
62-
return new PreDecorationFilter(routeLocator(), true);
71+
return new PreDecorationFilter(routeLocator(), this.server.getServletPrefix(),
72+
new ZuulProperties());
6373
}
6474

6575
@Bean
@@ -68,28 +78,36 @@ public SimpleHostRoutingFilter simpleHostRoutingFilter() {
6878
if (this.traces != null) {
6979
helper.setTraces(this.traces);
7080
}
71-
return new SimpleHostRoutingFilter(helper);
81+
return new SimpleHostRoutingFilter(helper, new ZuulProperties());
7282
}
7383

7484
@Bean
7585
@Override
7686
public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
77-
return new ZuulRefreshListener();
87+
return new ZuulRefreshListener(zuulHandlerMapping);
88+
}
89+
90+
@Configuration
91+
@ConditionalOnClass(Endpoint.class)
92+
protected static class RoutesEndpointConfiguration {
93+
@Bean
94+
public RoutesEndpoint zuulEndpoint(RouteLocator routeLocator) {
95+
return new RoutesEndpoint(routeLocator);
96+
}
7897
}
7998

8099
private static class ZuulRefreshListener implements ApplicationListener<ApplicationEvent> {
81-
@Autowired
82100
private ZuulHandlerMapping zuulHandlerMapping;
83101

84-
@Autowired
85-
private ApplicationRouteLocator routeLocator;
102+
public ZuulRefreshListener(ZuulHandlerMapping zuulHandlerMapping) {
103+
this.zuulHandlerMapping = zuulHandlerMapping;
104+
}
86105

87106
@Override
88107
public void onApplicationEvent(ApplicationEvent event) {
89108
if (event instanceof PayloadApplicationEvent && ((PayloadApplicationEvent<?>) event)
90109
.getPayload() instanceof RoutesOutdatedEvent) {
91-
routeLocator.resetRoutes();
92-
zuulHandlerMapping.registerHandlers();
110+
zuulHandlerMapping.setDirty(true);
93111
}
94112
}
95113
}

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/discovery/ApplicationDiscoveryListener.java

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,21 @@
2222
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
2323
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
2424
import org.springframework.context.event.EventListener;
25-
import org.springframework.util.StringUtils;
2625

2726
import de.codecentric.boot.admin.model.Application;
2827
import de.codecentric.boot.admin.registry.ApplicationRegistry;
2928

3029
/**
3130
* Listener for Heartbeats events to publish all services to the application registry.
3231
*
33-
* @author Johannes Stelzer
32+
* @author Johannes Edmeier
3433
*/
3534
public class ApplicationDiscoveryListener {
3635

3736
private final DiscoveryClient discoveryClient;
38-
3937
private final ApplicationRegistry registry;
40-
4138
private final HeartbeatMonitor monitor = new HeartbeatMonitor();
42-
43-
private String managementContextPath = "";
44-
45-
private String serviceContextPath = "";
46-
47-
private String healthEndpoint = "health";
39+
private ServiceInstanceConverter converter = new DefaultServiceInstanceConverter();
4840

4941
public ApplicationDiscoveryListener(DiscoveryClient discoveryClient,
5042
ApplicationRegistry registry) {
@@ -82,33 +74,10 @@ protected void discover() {
8274
}
8375

8476
protected Application convert(ServiceInstance instance) {
85-
String serviceUrl = append(instance.getUri().toString(), serviceContextPath);
86-
String managementUrl = append(instance.getUri().toString(), managementContextPath);
87-
String healthUrl = append(managementUrl, healthEndpoint);
88-
89-
return Application.create(instance.getServiceId()).withHealthUrl(healthUrl)
90-
.withManagementUrl(managementUrl).withServiceUrl(serviceUrl).build();
91-
}
92-
93-
public void setManagementContextPath(String managementContextPath) {
94-
this.managementContextPath = managementContextPath;
95-
}
96-
97-
public void setServiceContextPath(String serviceContextPath) {
98-
this.serviceContextPath = serviceContextPath;
77+
return converter.convert(instance);
9978
}
10079

101-
public void setHealthEndpoint(String healthEndpoint) {
102-
this.healthEndpoint = healthEndpoint;
103-
}
104-
105-
protected final String append(String uri, String path) {
106-
String baseUri = uri.replaceFirst("/+$", "");
107-
if (StringUtils.isEmpty(path)) {
108-
return baseUri;
109-
}
110-
111-
String normPath = path.replaceFirst("^/+", "").replaceFirst("/+$", "");
112-
return baseUri + "/" + normPath;
80+
public void setConverter(ServiceInstanceConverter converter) {
81+
this.converter = converter;
11382
}
11483
}

0 commit comments

Comments
 (0)