Skip to content

Commit

Permalink
Merge pull request #5 from codecentric/master
Browse files Browse the repository at this point in the history
Create own Jackson module for Spring Boot Admin Server (codecentric#1404
  • Loading branch information
igit-cn authored Aug 4, 2020
2 parents 4aa4bc9 + f2d7c82 commit e1d82f0
Show file tree
Hide file tree
Showing 298 changed files with 13,296 additions and 5,167 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ codecentric's Spring Boot Admin

This community project provides an admin interface for [Spring Boot <sup>®</sup>](http://projects.spring.io/spring-boot/ "Official Spring-Boot website") applications.

It provides the following features for registered applications.
Monitoring Python applications is available using [Pyctuator](https://github.com/SolarEdgeTech/pyctuator).

Spring Boot Admin provides the following features for registered applications:

* Show health status
* Show details, like
Expand Down Expand Up @@ -38,7 +40,7 @@ It provides the following features for registered applications.

## Getting Started

[A quick guide](http://codecentric.github.io/spring-boot-admin/2.1.6/#getting-started) to get started can be found in our docs.
[A quick guide](http://codecentric.github.io/spring-boot-admin/2.2.3/#getting-started) to get started can be found in our docs.

There are introductory talks available on YouTube:

Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<description>Spring Boot Admin</description>
<url>https://github.com/codecentric/spring-boot-admin/</url>
<properties>
<revision>2.2.0-SNAPSHOT</revision>
<revision>2.3.0-SNAPSHOT</revision>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
Expand All @@ -35,8 +35,8 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<!-- used dependencies versions -->
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR2</spring-cloud.version>
<spring-boot.version>2.3.1.RELEASE</spring-boot.version>
<spring-cloud.version>2020.0.0-M1</spring-cloud.version>
<wiremock.version>2.26.1</wiremock.version>
<hazelcast-tests.version>3.12.6</hazelcast-tests.version>
<findbugs-jsr305.version>3.0.2</findbugs-jsr305.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import de.codecentric.boot.admin.client.registration.ApplicationRegistrator;
import de.codecentric.boot.admin.client.registration.BlockingRegistrationClient;
import de.codecentric.boot.admin.client.registration.DefaultApplicationFactory;
import de.codecentric.boot.admin.client.registration.DefaultApplicationRegistrator;
import de.codecentric.boot.admin.client.registration.ReactiveRegistrationClient;
import de.codecentric.boot.admin.client.registration.RegistrationApplicationListener;
import de.codecentric.boot.admin.client.registration.RegistrationClient;
Expand All @@ -73,7 +74,7 @@ public class SpringBootAdminClientAutoConfiguration {
public ApplicationRegistrator registrator(RegistrationClient registrationClient, ClientProperties client,
ApplicationFactory applicationFactory) {

return new ApplicationRegistrator(applicationFactory, registrationClient, client.getAdminUrl(),
return new DefaultApplicationRegistrator(applicationFactory, registrationClient, client.getAdminUrl(),
client.isRegisterOnce());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,119 +16,26 @@

package de.codecentric.boot.admin.client.registration;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.LongAdder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Registers the client application at spring-boot-admin-server
* Interface for client application registration at spring-boot-admin-server
*/
public class ApplicationRegistrator {

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

private final ConcurrentHashMap<String, LongAdder> attempts = new ConcurrentHashMap<>();

private final AtomicReference<String> registeredId = new AtomicReference<>();

private final ApplicationFactory applicationFactory;

private final String[] adminUrls;

private final boolean registerOnce;

private final RegistrationClient registrationClient;

public ApplicationRegistrator(ApplicationFactory applicationFactory, RegistrationClient registrationClient,
String[] adminUrls, boolean registerOnce) {
this.applicationFactory = applicationFactory;
this.adminUrls = adminUrls;
this.registerOnce = registerOnce;
this.registrationClient = registrationClient;
}
public interface ApplicationRegistrator {

/**
* Registers the client application at spring-boot-admin-server.
* @return true if successful registration on at least one admin server
*/
public boolean register() {
Application application = this.applicationFactory.createApplication();
boolean isRegistrationSuccessful = false;

for (String adminUrl : this.adminUrls) {
LongAdder attempt = this.attempts.computeIfAbsent(adminUrl, (k) -> new LongAdder());
boolean successful = register(application, adminUrl, attempt.intValue() == 0);

if (!successful) {
attempt.increment();
}
else {
attempt.reset();
isRegistrationSuccessful = true;
if (this.registerOnce) {
break;
}
}
}

return isRegistrationSuccessful;
}
boolean register();

protected boolean register(Application application, String adminUrl, boolean firstAttempt) {
try {
String id = this.registrationClient.register(adminUrl, application);
if (this.registeredId.compareAndSet(null, id)) {
LOGGER.info("Application registered itself as {}", id);
}
else {
LOGGER.debug("Application refreshed itself as {}", id);
}
return true;
}
catch (Exception ex) {
if (firstAttempt) {
LOGGER.warn(
"Failed to register application as {} at spring-boot-admin ({}): {}. Further attempts are logged on DEBUG level",
application, this.adminUrls, ex.getMessage());
}
else {
LOGGER.debug("Failed to register application as {} at spring-boot-admin ({}): {}", application,
this.adminUrls, ex.getMessage());
}
return false;
}
}

public void deregister() {
String id = this.registeredId.get();
if (id == null) {
return;
}

for (String adminUrl : this.adminUrls) {
try {
this.registrationClient.deregister(adminUrl, id);
this.registeredId.compareAndSet(id, null);
if (this.registerOnce) {
break;
}
}
catch (Exception ex) {
LOGGER.warn("Failed to deregister application (id={}) at spring-boot-admin ({}): {}", id, adminUrl,
ex.getMessage());
}
}
}
/**
* Tries to deregister currently registered application
*/
void deregister();

/**
* @return the id of this client as given by the admin server. Returns null if the
* client has not registered against the admin server yet.
*/
public String getRegisteredId() {
return this.registeredId.get();
}
String getRegisteredId();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2014-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package de.codecentric.boot.admin.client.registration;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.LongAdder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultApplicationRegistrator implements ApplicationRegistrator {

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

private final ConcurrentHashMap<String, LongAdder> attempts = new ConcurrentHashMap<>();

private final AtomicReference<String> registeredId = new AtomicReference<>();

private final ApplicationFactory applicationFactory;

private final String[] adminUrls;

private final boolean registerOnce;

private final RegistrationClient registrationClient;

public DefaultApplicationRegistrator(ApplicationFactory applicationFactory, RegistrationClient registrationClient,
String[] adminUrls, boolean registerOnce) {
this.applicationFactory = applicationFactory;
this.adminUrls = adminUrls;
this.registerOnce = registerOnce;
this.registrationClient = registrationClient;
}

/**
* Registers the client application at spring-boot-admin-server.
* @return true if successful registration on at least one admin server
*/
@Override
public boolean register() {
Application application = this.applicationFactory.createApplication();
boolean isRegistrationSuccessful = false;

for (String adminUrl : this.adminUrls) {
LongAdder attempt = this.attempts.computeIfAbsent(adminUrl, (k) -> new LongAdder());
boolean successful = register(application, adminUrl, attempt.intValue() == 0);

if (!successful) {
attempt.increment();
}
else {
attempt.reset();
isRegistrationSuccessful = true;
if (this.registerOnce) {
break;
}
}
}

return isRegistrationSuccessful;
}

protected boolean register(Application application, String adminUrl, boolean firstAttempt) {
try {
String id = this.registrationClient.register(adminUrl, application);
if (this.registeredId.compareAndSet(null, id)) {
LOGGER.info("Application registered itself as {}", id);
}
else {
LOGGER.debug("Application refreshed itself as {}", id);
}
return true;
}
catch (Exception ex) {
if (firstAttempt) {
LOGGER.warn(
"Failed to register application as {} at spring-boot-admin ({}): {}. Further attempts are logged on DEBUG level",
application, this.adminUrls, ex.getMessage());
}
else {
LOGGER.debug("Failed to register application as {} at spring-boot-admin ({}): {}", application,
this.adminUrls, ex.getMessage());
}
return false;
}
}

@Override
public void deregister() {
String id = this.registeredId.get();
if (id == null) {
return;
}

for (String adminUrl : this.adminUrls) {
try {
this.registrationClient.deregister(adminUrl, id);
this.registeredId.compareAndSet(id, null);
if (this.registerOnce) {
break;
}
}
catch (Exception ex) {
LOGGER.warn("Failed to deregister application (id={}) at spring-boot-admin ({}): {}", id, adminUrl,
ex.getMessage());
}
}
}

@Override
public String getRegisteredId() {
return this.registeredId.get();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
import java.net.UnknownHostException;
import java.util.concurrent.CountDownLatch;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.common.ConsoleNotifier;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand All @@ -41,23 +41,27 @@
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;

public abstract class AbstractClientApplicationTest {

@Rule
public WireMockRule wireMock = new WireMockRule(options().dynamicPort().notifier(new ConsoleNotifier(true)));
public WireMockServer wireMock = new WireMockServer(options().dynamicPort().notifier(new ConsoleNotifier(true)));

private static final CountDownLatch cdl = new CountDownLatch(1);

public void setUp() throws Exception {
wireMock.start();
ResponseDefinitionBuilder response = created().withHeader("Content-Type", "application/json")
.withHeader("Connection", "close").withHeader("Location", wireMock.url("/instances/abcdef"))
.withBody("{ \"id\" : \"abcdef\" }");
wireMock.stubFor(post(urlEqualTo("/instances")).willReturn(response));
}

@AfterEach
void tearDown() {
wireMock.stop();
}

@Test
public void test_context() throws InterruptedException, UnknownHostException {
cdl.await();
Expand All @@ -73,7 +77,7 @@ public void test_context() throws InterruptedException, UnknownHostException {
.withRequestBody(matchingJsonPath("$.serviceUrl", equalTo(serviceHost + "/")))
.withRequestBody(matchingJsonPath("$.metadata.startup", matching(".+")));

verify(request);
wireMock.verify(request);
}

protected abstract int getServerPort();
Expand Down
Loading

0 comments on commit e1d82f0

Please sign in to comment.