This is the codeAbility Sharing Platform! Learn more about the codeAbility Sharing Platform.

Skip to content
Snippets Groups Projects
Commit e880b540 authored by Michael Breu's avatar Michael Breu :speech_balloon:
Browse files

Extending health checks for Plugins

parent 1ac8c9e6
2 merge requests!188Merging Peer Reviewing et. al to Master,!164211 peer reviewing functionality
......@@ -32,6 +32,7 @@
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="module" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
......@@ -40,10 +41,5 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/SharingPluginPlattform">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
......@@ -3,6 +3,7 @@ package at.ac.uibk.gitsearch.service;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
......@@ -46,31 +47,35 @@ import at.ac.uibk.gitsearch.service.dto.SearchResultDTO.PluginActionInfo;
@Service
public class PluginManagementService {
private final Map<String, Exception> configFailures = new HashMap<>();
private final class PluginCheckTimer extends TimerTask {
private final String registeredPluginURL;
private PluginCheckTimer(String registeredPlugin) {
this.registeredPluginURL = registeredPlugin;
}
@Override
public void run() {
ClientConfig restClientConfig = new ClientConfig();
restClientConfig.register(JacksonJsonProvider.class);
Client client = ClientBuilder.newClient(restClientConfig);
WebTarget target = client.target(registeredPluginURL);
SharingPluginConfig config = null;
try {
config = target.request().accept(MediaType.APPLICATION_JSON)
.get(SharingPluginConfig.class);
registeredPluginConfigs.put(config.pluginName, new PluginConfigWrapper(config, registeredPluginURL) );
}
catch (ProcessingException ce) {
log.warn("Cannot connect to plugin at {}: {}", registeredPluginURL, ce.getMessage());
}
catch (Exception e) {
log.error("Cannot (re-)load plugin at {}", registeredPluginURL, e);
synchronized (configFailures) {
ClientConfig restClientConfig = new ClientConfig();
restClientConfig.register(JacksonJsonProvider.class);
Client client = ClientBuilder.newClient(restClientConfig);
WebTarget target = client.target(registeredPluginURL);
SharingPluginConfig config = null;
try {
config = target.request().accept(MediaType.APPLICATION_JSON).get(SharingPluginConfig.class);
registeredPluginConfigs.put(config.pluginName,
new PluginConfigWrapper(config, registeredPluginURL));
configFailures.remove(registeredPluginURL);
} catch (ProcessingException ce) {
log.warn("Cannot connect to plugin at {}: {}", registeredPluginURL, ce.getMessage());
configFailures.put(registeredPluginURL, ce);
} catch (Exception e) {
log.warn("Cannot (re-)load plugin at {}", registeredPluginURL, e.getMessage());
configFailures.put(registeredPluginURL, e);
final Iterator<Entry<String, PluginConfigWrapper>> pluginIterator = registeredPluginConfigs
.entrySet().iterator();
while (pluginIterator.hasNext()) {
......@@ -80,8 +85,10 @@ public class PluginManagementService {
break;
}
}
}
}
}
}
......@@ -250,4 +257,11 @@ public class PluginManagementService {
return registeredPluginConfigs.values();
}
/**
* @return the configFailures
*/
public Map<String, Exception> getConfigFailures() {
return Collections.unmodifiableMap(configFailures);
}
}
......@@ -6,12 +6,19 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.health.HealthContributorRegistry;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import at.ac.uibk.gitsearch.config.ApplicationProperties;
......@@ -20,29 +27,52 @@ import at.ac.uibk.gitsearch.service.PluginManagementService.PluginConfigWrapper;
@Service
//@Endpoint(id = "pluginHealth")
public class PluginHealthCheck implements HealthIndicator {
public class PluginHealthCheckRegistry {
@Autowired
protected PluginManagementService pluginManagementService;
@Autowired
@Autowired
private ApplicationProperties applicationProperties;
@Autowired
private HealthContributorRegistry healthContributorRegistry;
@PostConstruct
protected void registerPluginHealth() {
for (String configURL : applicationProperties.getRegisteredPlugins()) {
healthContributorRegistry.registerContributor(configURL,
new PluginHealthCheck(configURL, pluginManagementService));
}
}
public static class PluginHealthCheck implements HealthContributor, HealthIndicator {
protected PluginManagementService pluginManagementService;
private String configURL;
public PluginHealthCheck(String configURL, PluginManagementService pluginManagementService) {
super();
this.configURL = configURL;
this.pluginManagementService = pluginManagementService;
}
public Health health() {
for (PluginConfigWrapper pluginConfig : pluginManagementService.getRegisteredPluginConfigs()) {
if (configURL.equals(pluginConfig.getConfigURL())) {
final String message = String.format("Running as %s with %d actions", pluginConfig.getPluginName(),
pluginConfig.getActions().size());
return Health.up().withDetail(pluginConfig.getPluginName(), message).build();
}
}
for (Map.Entry<String, Exception> failures : pluginManagementService.getConfigFailures().entrySet()) {
if (configURL.equals(failures.getKey())) {
return Health.down(failures.getValue()).withDetail(configURL, "has failed").build();
}
}
@Override
public Health health() {
Map<String, String> visited = new HashMap<>();
for(PluginConfigWrapper pluginConfig: pluginManagementService.getRegisteredPluginConfigs()) {
final String message = String.format("Running as %s with %d actions", pluginConfig.getPluginName(), pluginConfig.getActions().size());
visited.put(pluginConfig.getConfigURL(), message);
}
boolean down = visited.size() < applicationProperties.getRegisteredPlugins().size(); // some missing?
applicationProperties.getRegisteredPlugins().stream().filter(configURL -> !visited.containsKey(configURL))
.forEach(configURL -> visited.put(configURL, String.format("seems to be down")));
return (down?Health.down():Health.up()).withDetails(visited).build();
return Health.unknown().build();
}
}
}
......@@ -19,7 +19,7 @@
<tbody *ngIf="health">
<tr *ngFor="let componentHealth of health.components | keys">
<td>
{{ 'health.indicator.' + componentHealth.key | translate }}
{{ translateKeys( 'health.indicator.' + componentHealth.key ) }}
</td>
<td class="text-center">
<span class="badge" [ngClass]="getBadgeClass(componentHealth.value.status)" jhiTranslate="{{ 'health.status.' + componentHealth.value.status }}">
......
import { Component, OnInit } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateService } from '@ngx-translate/core';
import { HealthService, HealthStatus, Health, HealthKey, HealthDetails } from './health.service';
import { HealthModalComponent } from './health-modal.component';
......@@ -12,7 +13,7 @@ import { HealthModalComponent } from './health-modal.component';
export class HealthComponent implements OnInit {
health?: Health;
constructor(private modalService: NgbModal, private healthService: HealthService) {}
constructor(private modalService: NgbModal, private healthService: HealthService, private translateService: TranslateService) {}
ngOnInit(): void {
this.refresh();
......@@ -41,4 +42,9 @@ export class HealthComponent implements OnInit {
const modalRef = this.modalService.open(HealthModalComponent);
modalRef.componentInstance.health = health;
}
translateKeys(key: string) {
if ( key.startsWith('health.indicator.http')) return key.substr(17);
return this.translateService.instant(key);
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment