package net.unit8.waitt.feature.dashboard;
import com.sun.management.HotSpotDiagnosticMXBean;
import net.unit8.waitt.api.configuration.WebappConfiguration;
import net.unit8.waitt.api.dto.ServerMetadata;
import org.gridkit.jvmtool.heapdump.HeapHistogram;
import org.gridkit.jvmtool.heapdump.StringCollector;
import org.netbeans.lib.profiler.heap.FastHprofHeap;
import org.netbeans.lib.profiler.heap.Heap;
import spark.*;
import spark.servlet.SparkApplication;
import spark.template.thymeleaf.ThymeleafTemplateEngine;
import javax.xml.bind.JAXB;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import static spark.Spark.*;
/**
* @author kawasima
*/
public class DashboardApplication implements SparkApplication {
@Override
public void init() {
final AdminConfig adminConfig = new AdminConfig();
adminConfig.read();
TemplateEngine engine = new ThymeleafTemplateEngine();
staticFileLocation("/public");
get("/", new TemplateViewRoute() {
@Override
public ModelAndView handle(Request request, Response response) throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>();
if (adminConfig.isAdminAvailable()) {
HttpURLConnection conn = (HttpURLConnection) new URL("http://localhost:" + adminConfig.getAdminPort() + "/app").openConnection();
InputStream in = conn.getInputStream();
try {
WebappConfiguration config = JAXB.unmarshal(in, WebappConfiguration.class);
attributes.put("webappConfiguration", config);
} finally {
in.close();
conn.disconnect();
}
}
attributes.put("adminAvailable", adminConfig.isAdminAvailable());
attributes.put("context", request.contextPath());
return new ModelAndView(attributes, "application");
}
}, engine);
get("/server", new TemplateViewRoute() {
@Override
public ModelAndView handle(Request request, Response response) throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>();
if (adminConfig.isAdminAvailable()) {
InputStream in = new URL("http://localhost:" + adminConfig.getAdminPort() + "/server").openStream();
try {
ServerMetadata metadata = JAXB.unmarshal(in, ServerMetadata.class);
attributes.put("serverMetadata", metadata);
} finally {
in.close();
}
}
attributes.put("adminAvailable", adminConfig.isAdminAvailable());
attributes.put("context", request.contextPath());
return new ModelAndView(attributes, "server");
}
}, engine);
post("/server/reload", new Route() {
@Override
public Object handle(Request request, Response response) throws Exception {
if (!adminConfig.isAdminAvailable()) {
response.status(403);
response.body("Admin feature is unavailable.");
return null;
}
HttpURLConnection conn = (HttpURLConnection) new URL("http://localhost:" + adminConfig.getAdminPort() + "/reload").openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
try {
conn.getOutputStream().close();
InputStream is = conn.getInputStream();
try {
//noinspection StatementWithEmptyBody
while (is.read() >= 0) ;
is.close();
} finally {
is.close();
}
response.redirect(request.contextPath()
+ "/server");
return null;
} catch (Exception e) {
response.status(500);
return null;
} finally {
conn.disconnect();
}
}
});
get("/env", new TemplateViewRoute() {
@Override
public ModelAndView handle(Request request, Response response) throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put("environments", System.getenv());
attributes.put("context", request.contextPath());
return new ModelAndView(attributes, "env");
}
}, engine);
get("/property", new TemplateViewRoute() {
@Override
public ModelAndView handle(Request request, Response response) throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put("properties", System.getProperties());
attributes.put("context", request.contextPath());
return new ModelAndView(attributes, "property");
}
}, engine);
get("/heap", new TemplateViewRoute() {
@Override
public ModelAndView handle(Request request, Response response) throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put("heapdump", heapDump());
attributes.put("context", request.contextPath());
return new ModelAndView(attributes, "heap");
}
}, engine);
get("/thread", new TemplateViewRoute() {
@Override
public ModelAndView handle(Request rqst, Response rspns) throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put("threaddump", threadDump());
attributes.put("menu", "heap");
return new ModelAndView(attributes, "thread");
}
}, engine);
}
private List<HeapHistogram.ClassRecord> heapDump() throws IOException {
File heapDumpDirectory = new File("target");
File dumpFile = null;
try {
dumpFile = File.createTempFile("waitt", ".hprof", heapDumpDirectory);
if (dumpFile.exists()) {
dumpFile.delete();
}
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(ManagementFactory.getPlatformMBeanServer(), "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(dumpFile.getPath(), true);
Heap heap = new FastHprofHeap(dumpFile, 0);
StringCollector collector = new StringCollector();
HeapHistogram histo = new HeapHistogram();
collector.collect(heap, histo);
List<HeapHistogram.ClassRecord> ht = new ArrayList<HeapHistogram.ClassRecord>(histo.getHisto());
ht.add(collector.asClassRecord());
Collections.sort(ht, HeapHistogram.BY_SIZE);
return ht.subList(0, 500);
} finally {
if (dumpFile != null && dumpFile.exists()) {
dumpFile.delete();
}
}
}
private List<ThreadInfo> threadDump() {
ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = mxBean.getThreadInfo(mxBean.getAllThreadIds(), 0);
return Arrays.asList(threadInfos);
}
}