Updated directory structure

This commit is contained in:
2020-07-07 23:00:19 +02:00
parent ce00bd39ee
commit ef60abc4f9
26 changed files with 44 additions and 61 deletions

View File

@ -0,0 +1,29 @@
package de.twomartens.oparlservice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Optional;
@Slf4j
@SpringBootApplication
@EnableScheduling
public class OParlServiceApplication {
public static void main(String[] args) {
log.info(">>> {}:{} <<<", getTitle(), getVersion());
SpringApplication.run(OParlServiceApplication.class, args);
}
private static String getTitle() {
return Optional.ofNullable(OParlServiceApplication.class.getPackage().getImplementationTitle())
.orElse("start");
}
private static String getVersion() {
return Optional.ofNullable(OParlServiceApplication.class.getPackage().getImplementationVersion())
.orElse("snapshot");
}
}

View File

@ -0,0 +1,68 @@
package de.twomartens.oparlservice.actuator;
import de.twomartens.oparlservice.interceptors.RequestTypeInterceptor;
import de.twomartens.oparlservice.interceptors.TraceIdInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
@Slf4j
public abstract class AbstractHealthCheck implements HealthIndicator {
private static final String DEFAULT_HOST = "localhost";
private static final String HEALTH_KEY_ENDPOINT = "endpoint";
private final String endpoint;
private final TraceIdInterceptor traceIdInterceptor;
private final RequestTypeInterceptor requestTypeInterceptor;
private final int port;
public AbstractHealthCheck(int port,
TraceIdInterceptor traceIdInterceptor,
RequestTypeInterceptor requestTypeInterceptor) {
this.traceIdInterceptor = traceIdInterceptor;
this.requestTypeInterceptor = requestTypeInterceptor;
this.port = port;
this.endpoint = mkEndpoint();
}
@Override
public Health health() {
requestTypeInterceptor.markAsHealthCheck();
traceIdInterceptor.createNewTraceId();
Health result;
try {
if (isEndpointAvailable()) {
result = Health.up().withDetail(HEALTH_KEY_ENDPOINT, getEndpoint()).build();
} else {
result = Health.down().withDetail(HEALTH_KEY_ENDPOINT, getEndpoint()).build();
}
} catch (Exception e) {
result = Health.down().withDetail(HEALTH_KEY_ENDPOINT, getEndpoint()).withException(e).build();
}
log.info("health check invoked for '{}' with status '{}'", getMethodName(), result.getStatus());
return result;
}
String getHost() {
return DEFAULT_HOST;
}
int getPort() {
return this.port;
}
String getEndpoint() {
return this.endpoint;
}
abstract boolean isEndpointAvailable();
abstract String getMethodName();
abstract String mkEndpoint();
}

View File

@ -0,0 +1,56 @@
package de.twomartens.oparlservice.actuator;
import de.twomartens.oparlservice.entity.Greeting;
import de.twomartens.oparlservice.interceptors.RequestTypeInterceptor;
import de.twomartens.oparlservice.interceptors.TraceIdInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.util.Optional;
/**
* A Health check which checks if the rest services are working.]
*/
@Slf4j
@Component
public class RestHealthCheck extends AbstractHealthCheck implements HealthIndicator {
private final RestTemplate restTemplate;
public RestHealthCheck(ServerProperties serverProperties,
TraceIdInterceptor traceIdInterceptor,
RequestTypeInterceptor requestTypeInterceptor,
RestTemplate restTemplate) {
super(Optional.ofNullable(serverProperties.getPort()).orElse(8080),
traceIdInterceptor, requestTypeInterceptor);
this.restTemplate = restTemplate;
}
@Override
boolean isEndpointAvailable() {
try {
ResponseEntity<Greeting> result = restTemplate.getForEntity(getEndpoint(), Greeting.class);
Greeting body = result.getBody();
if (body == null) {
return false;
}
return !body.getMessage().isEmpty();
} catch (RestClientException e) {
return false;
}
}
@Override
String getMethodName() {
return "template-service-rest";
}
String mkEndpoint() {
return String.format("http://%s:%d/greeting?name=RestHealthCheck", getHost(), getPort());
}
}

View File

@ -0,0 +1,19 @@
package de.twomartens.oparlservice.configs;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "de.twomartens.oparlservice")
public class OParlServiceProperties {
private final Template template = new Template();
@Data
public static class Template {
private String greeting;
}
}

View File

@ -0,0 +1,47 @@
package de.twomartens.oparlservice.configs;
import de.twomartens.oparlservice.interceptors.RequestTypeInterceptor;
import de.twomartens.oparlservice.interceptors.TraceIdInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import java.util.ArrayList;
import java.util.List;
@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TraceIdInterceptor());
registry.addInterceptor(new RequestTypeInterceptor());
}
@Bean
public InternalResourceViewResolver defaultViewResolver() {
return new InternalResourceViewResolver();
}
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new TraceIdInterceptor());
interceptors.add(new RequestTypeInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}

View File

@ -0,0 +1,24 @@
package de.twomartens.oparlservice.control;
import de.twomartens.oparlservice.entity.Greeting;
import de.twomartens.oparlservice.service.GreetingService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private final GreetingService service;
GreetingController(GreetingService service) {
this.service = service;
}
@GetMapping("/greeting")
@ResponseBody
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return Greeting.builder().message(service.createGreeting(name)).build();
}
}

View File

@ -0,0 +1,18 @@
package de.twomartens.oparlservice.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
@Builder
@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
public class Greeting {
@NonNull
@Schema(description = "This message is displayed")
private final String message;
}

View File

@ -0,0 +1,52 @@
package de.twomartens.oparlservice.interceptors;
import lombok.NoArgsConstructor;
import org.slf4j.MDC;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* marks requests of certain types like health check or integration test for better logging
*/
@Component
@NoArgsConstructor
public class RequestTypeInterceptor extends HandlerInterceptorAdapter
implements ClientHttpRequestInterceptor {
private static final String LOGGER_ID = "REQTYPE";
private static final String HEADER_FIELD_ID = "x-type";
public void markAsHealthCheck() {
MDC.put(LOGGER_ID, "HEALTH_CHECK");
}
public void markAsIntegrationTest() {
MDC.put(LOGGER_ID, "INTEGRATION_TEST");
}
// web client interceptor
@Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add(HEADER_FIELD_ID, MDC.get(LOGGER_ID));
return execution.execute(request, body);
}
// web server interceptor
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
MDC.put(LOGGER_ID, request.getHeader(HEADER_FIELD_ID));
return true;
}
}

View File

@ -0,0 +1,87 @@
package de.twomartens.oparlservice.interceptors;
import lombok.NoArgsConstructor;
import org.slf4j.MDC;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
@Component
@NoArgsConstructor
public class TraceIdInterceptor extends HandlerInterceptorAdapter
implements ClientHttpRequestInterceptor {
private static final String LOGGER_TRACE_ID = "TraceID";
private static final String HEADER_FIELD_TRACE_ID = "X-TraceId";
/**
* Gets the TraceId from the MDC or create a new one and put it to the MDC
*/
public String getTraceId() {
String traceId = MDC.get(LOGGER_TRACE_ID);
if (traceId == null || traceId.trim().isEmpty()) {
traceId = UUID.randomUUID().toString();
MDC.put(LOGGER_TRACE_ID, traceId);
}
return traceId;
}
/**
* A failsafe method to set a TraceId to the MDC.
*
* @param traceId should be a traceId, if it is null or empty a new one will be created.
* @return the traceId param, or a new one, if it was empty
*/
public String setOrCreateTraceId(String traceId) {
if (traceId == null || traceId.trim().isEmpty()) {
traceId = UUID.randomUUID().toString();
}
MDC.put(LOGGER_TRACE_ID, traceId);
return traceId;
}
/**
* Removes the traceId from the MDC.
*/
public void resetTraceId() {
MDC.remove(LOGGER_TRACE_ID);
}
/**
* Creates a new traceId for this thread.
*
* An old one will be overwritten!
*/
public void createNewTraceId() {
resetTraceId();
getTraceId();
}
// client interceptor web
@Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add(HEADER_FIELD_TRACE_ID, getTraceId());
return execution.execute(request, body);
}
// server interceptor web
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String traceId = request.getHeader(HEADER_FIELD_TRACE_ID);
setOrCreateTraceId(traceId);
response.addHeader(HEADER_FIELD_TRACE_ID, getTraceId());
return true;
}
}

View File

@ -0,0 +1,30 @@
package de.twomartens.oparlservice.service;
import de.twomartens.oparlservice.configs.OParlServiceProperties;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class GreetingService {
private final MeterRegistry meterRegistry;
private final OParlServiceProperties properties;
private final Counter counter;
public GreetingService(MeterRegistry meterRegistry, OParlServiceProperties properties) {
this.meterRegistry = meterRegistry;
this.properties = properties;
counter = meterRegistry.counter("infodb.callCounter");
}
public String createGreeting(String name) {
log.info("Create greeting for '{}'", name);
counter.increment();
meterRegistry.gauge("infodb.nameLength", name.length());
String greeting = properties.getTemplate().getGreeting();
return String.format(greeting, name);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="warn" monitorInterval="30">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%5p %d{HH:mm:ss.SSS} (%F:%L) [%X{REQTYPE}] %m%n%xEx{full}"
alwaysWriteExceptions="true"/>
</Console>
</Appenders>
<Loggers>
<Root level="WARN">
<AppenderRef ref="console"/>
</Root>
<Logger name="de.twomartens" level="TRACE" additivity="true"/>
</Loggers>
</Configuration>