package io.mangoo.admin;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import com.google.inject.Inject;
import io.mangoo.annotations.FilterWith;
import io.mangoo.core.Application;
import io.mangoo.crypto.Crypto;
import io.mangoo.enums.Required;
import io.mangoo.enums.Template;
import io.mangoo.exceptions.MangooSchedulerException;
import io.mangoo.models.Job;
import io.mangoo.models.Metrics;
import io.mangoo.routing.Response;
import io.mangoo.routing.Route;
import io.mangoo.routing.Router;
import io.mangoo.routing.bindings.Request;
import io.mangoo.scheduler.Scheduler;
import io.mangoo.utils.BootstrapUtils;
import io.mangoo.utils.CodecUtils;
/**
* Controller class for administrative URLs
*
* @author svenkubiak
*
*/
@FilterWith(AdminFilter.class)
public class AdminController {
private static final org.apache.logging.log4j.Logger LOG = LogManager.getLogger(AdminController.class);
private static final String SCHEDULER = "scheduler";
private static final String METRICS = "metrics"; //NOSONAR
private static final String ROUTES = "routes"; //NOSONAR
private static final String JOBS = "jobs";
private static final String LOGGER = "logger";
private static final String TOOLS = "tools";
private static final String SPACE = "space";
private static final String VERSION = "version";
private final Scheduler scheduler; //NOSONAR
private final Crypto crypto; //NOSONAR
@Inject
public AdminController(Scheduler scheduler, Crypto crypto) {
this.scheduler = Objects.requireNonNull(scheduler, Required.SCHEDULER.toString());
this.crypto = Objects.requireNonNull(crypto, Required.CRYPTO.toString());
}
public Response index() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long allocatedMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
Instant instant = Application.getStart().atZone(ZoneId.systemDefault()).toInstant();
return Response.withOk()
.andContent(VERSION, BootstrapUtils.getVersion())
.andContent(SPACE, null)
.andContent("uptime", Date.from(instant))
.andContent("started", Application.getStart())
.andContent("maxMemory", FileUtils.byteCountToDisplaySize(maxMemory))
.andContent("allocatedMemory", FileUtils.byteCountToDisplaySize(allocatedMemory))
.andContent("freeMemory", FileUtils.byteCountToDisplaySize(freeMemory))
.andContent("totalFreeMemory", FileUtils.byteCountToDisplaySize(freeMemory + (maxMemory - allocatedMemory)))
.andTemplate(Template.DEFAULT.adminPath());
}
public Response execute(String name) {
try {
this.scheduler.executeJob(name);
} catch (MangooSchedulerException e) {
LOG.error("Failed to execute job with name: " + name, e);
}
return Response.withRedirect("/@admin/scheduler");
}
public Response state(String name) {
try {
this.scheduler.changeState(name);
} catch (MangooSchedulerException e) {
LOG.error("Failed to change the state of job with name: " + name, e);
}
return Response.withRedirect("/@admin/scheduler");
}
public Response routes() {
Set<Route> routes = Router.getRoutes()
.stream()
.filter(route -> !route.getUrl().contains("@admin"))
.collect(Collectors.toSet());
return Response.withOk()
.andContent(SPACE, ROUTES)
.andContent(VERSION, BootstrapUtils.getVersion())
.andContent(ROUTES, routes)
.andTemplate(Template.DEFAULT.routesPath());
}
public Response tools() {
return Response.withOk()
.andContent(SPACE, TOOLS)
.andContent(VERSION, BootstrapUtils.getVersion())
.andTemplate(Template.DEFAULT.toolsPath());
}
public Response logger() {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
return Response.withOk()
.andContent(SPACE, LOGGER)
.andContent(VERSION, BootstrapUtils.getVersion())
.andContent("loggers", loggerContext.getLoggers())
.andTemplate(Template.DEFAULT.loggerPath());
}
public Response loggerajax(Request request) {
Map<String, Object> body = request.getBodyAsJsonMap();
if (body != null && body.size() > 0) {
String clazz = body.get("class").toString();
String level = body.get("level").toString();
if (StringUtils.isNotBlank(clazz) && StringUtils.isNotBlank(level)) {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
for (Logger logger : loggerContext.getLoggers()) { //NOSONAR
if (clazz.equals(logger.getName())) {
logger.setLevel(Level.getLevel(level));
break;
}
}
}
}
return Response.withOk().andEmptyBody();
}
public Response toolsajax(Request request) {
Map<String, Object> body = request.getBodyAsJsonMap();
String value = "";
if (body != null && body.size() > 0) {
String function = body.get("function").toString();
String cleartext = body.get("cleartext").toString();
String key = body.get("key").toString();
if (("hash").equalsIgnoreCase(function)) {
value = CodecUtils.hexJBcrypt(cleartext);
} else if (("encrypt").equalsIgnoreCase(function)) {
if (StringUtils.isNotBlank(key)) {
value = this.crypto.encrypt(cleartext, key);
} else {
value = this.crypto.encrypt(cleartext);
}
}
}
return Response.withOk()
.andJsonBody(value);
}
public Response metrics() {
Metrics metrics = Application.getInstance(Metrics.class);
long totalRequests = 0;
long errorRequests = 0;
double errorRate = 0;
for (Entry<Integer, LongAdder> entry : metrics.getMetrics().entrySet()) {
if (String.valueOf(entry.getKey()).charAt(0) == '5') {
errorRequests = errorRequests + entry.getValue().longValue();
}
totalRequests = totalRequests + entry.getValue().longValue();
}
if (errorRequests > 0) {
errorRate = totalRequests / (double) errorRequests;
}
return Response.withOk()
.andContent(SPACE, METRICS)
.andContent(VERSION, BootstrapUtils.getVersion())
.andContent(METRICS, metrics.getMetrics())
.andContent("totalRequests", totalRequests)
.andContent("minRequestTime", metrics.getMinRequestTime())
.andContent("avgRequestTime", metrics.getAvgRequestTime())
.andContent("maxRequestTime", metrics.getMaxRequestTime())
.andContent("errorRate", errorRate)
.andTemplate(Template.DEFAULT.metricsPath());
}
public Response scheduler() {
List<Job> jobs = new ArrayList<>();
if (this.scheduler.isInitialize()) {
try {
jobs = this.scheduler.getAllJobs();
} catch (MangooSchedulerException e) {
LOG.error("Failed to retrieve jobs from scheduler", e);
}
}
return Response.withOk()
.andContent(SPACE, SCHEDULER)
.andContent(VERSION, BootstrapUtils.getVersion())
.andContent(JOBS, jobs)
.andTemplate(Template.DEFAULT.schedulerPath());
}
}