/** * Copyright (C) 2013 Arman Gal * * 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 org.clevermore.monitor.client.servers; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import org.clevermore.monitor.client.ServerWidgetService; import org.clevermore.monitor.client.ServerWidgetServiceAsync; import org.clevermore.monitor.client.utils.ClientStringFormatter; import org.clevermore.monitor.client.widgets.DynamicLine; import org.clevermore.monitor.client.widgets.ILineType; import org.clevermore.monitor.client.widgets.MonitoringDynamicLinesChart; import org.clevermore.monitor.client.widgets.MonitoringLineChart; import org.clevermore.monitor.shared.ChartFeed; import org.clevermore.monitor.shared.config.ClientConfigurations; import org.clevermore.monitor.shared.config.Colors; import org.clevermore.monitor.shared.runtime.CpuUtilizationChunk; import org.clevermore.monitor.shared.runtime.MemoryState; import org.clevermore.monitor.shared.runtime.MemoryUsage; import org.clevermore.monitor.shared.runtime.RuntimeInfo; import org.clevermore.monitor.shared.runtime.ThreadDump; import org.clevermore.monitor.shared.servers.ConnectedServer; import com.allen_sauer.gwt.log.client.Log; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.RepeatingCommand; import com.google.gwt.core.shared.GWT; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.FontWeight; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.i18n.client.HasDirection.Direction; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.RadioButton; import com.google.gwt.user.client.ui.TextArea; import com.google.gwt.user.client.ui.Widget; import com.googlecode.gwt.charts.client.ColumnType; public class ServerStatsPopup<CC extends ClientConfigurations> extends DialogBox { private final ServerWidgetServiceAsync service = GWT.create(ServerWidgetService.class); private MonitoringLineChart<Double, Long> cpuChart = new MonitoringLineChart<Double, Long>(new ILineType[] {ServersLineType.CPU}, "CPU%", "Time", "CPU Load"); private MonitoringLineChart<Double, Long> sysLoadChart = new MonitoringLineChart<Double, Long>(new ILineType[] {ServersLineType.SYS_LOAD}, "SysLoadAvg", "Time", "System Load Average"); private MonitoringLineChart<Double, Long> memoryChart = new MonitoringLineChart<Double, Long>(new ILineType[] {ServersLineType.MEMORY}, "Memory%", "Time", "Memory Usage"); private MonitoringDynamicLinesChart<Long, Long> memoryDetailsChart = new MonitoringDynamicLinesChart<Long, Long>("M.B.", "Time", "Memory Details"); private FlowPanel fp = new FlowPanel(); private FlowPanel cpu = new FlowPanel(); private FlowPanel memory = new FlowPanel(); private FlowPanel memoryDetails = new FlowPanel(); private FlowPanel sysLoad = new FlowPanel(); private FlowPanel details = new FlowPanel(); private Integer serverCode; private Integer chunks = 30; private boolean showHeap = true; private boolean refresh = true; private LinkedList<MemoryUsage> memoryUsages;// local copy for fast refresh NativePreviewHandler globalKeyHandler = new NativePreviewHandler() { @Override public void onPreviewNativeEvent(NativePreviewEvent event) { NativeEvent ne = event.getNativeEvent(); if (ne.getKeyCode() == 27) {//on Esc hide(); } } }; HandlerRegistration nativePreviewHandler = Event.addNativePreviewHandler(globalKeyHandler); public ServerStatsPopup(final Integer serverCode) { this.serverCode = serverCode; setAnimationEnabled(true); setModal(true); setSize("760px", "450px"); setGlassEnabled(true); service.getConnectedServer(serverCode, new AsyncCallback<ConnectedServer>() { public void onSuccess(ConnectedServer cs) { fp.add(new HTML("<h1>Server:" + cs.getServerCode() + ", " + cs.getName() + "</h1>")); fp.add(new HTML("<h2>Up Time:" + ClientStringFormatter.formatMilisecondsToHours(cs.getUpTime()) + "</h2>")); String gcs = ""; for (Double gch : cs.getGcHistories()) { gcs += ClientStringFormatter.formatMillisShort(gch) + ";"; } HTML tech = new HTML("<h2>Memory:" + ClientStringFormatter.formatMBytes(cs.getMemoryUsage().getUsed()) + " of " + ClientStringFormatter.formatMBytes(cs.getMemoryUsage().getMax()) + " MB, Usage:" + ClientStringFormatter.formatMillisShort(cs.getMemoryUsage().getPercentage()) + "%, GC Time:" + gcs + "</h2>"); fp.add(tech); HTML info = new HTML("<h2>" + cs.getMoreInfo() + "</h2"); fp.add(info); fp.getElement().setId("xxx"); setWidget(fp); memoryChart.setStyleName("serverPopupChart"); memory.add(memoryChart); memoryDetailsChart.setStyleName("serverPopupChart"); final CheckBox heap = new CheckBox("Show Heap"); heap.setValue(true); heap.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { showHeap = heap.getValue(); if (memoryUsages != null) { updateMemoryDetailsChart(memoryUsages); } } }); memoryDetails.add(heap); memoryDetails.add(memoryDetailsChart); cpuChart.setStyleName("serverPopupChart"); cpu.add(cpuChart); sysLoadChart.setStyleName("serverPopupChart"); sysLoad.add(sysLoadChart); loadSecondPart(); }; @Override public void onFailure(Throwable caught) { Window.alert("Error loading server:" + caught.getMessage()); Log.error("Error loading server:" + serverCode + ", Error:" + caught.getMessage(), caught); hide(); } }); } private void addRadioButtons(HorizontalPanel hp) { RadioButton r30m = getRadioButton("m30", 30); r30m.setValue(true); hp.add(r30m); hp.add(getRadioButton("1h", 60)); hp.add(getRadioButton("2h", 120)); hp.add(getRadioButton("6h", 360)); hp.add(getRadioButton("12h", 720)); hp.add(getRadioButton("1d", 1440)); } private RadioButton getRadioButton(final String name, int chunksToSet) { RadioButton r = new RadioButton("chunks", name); r.getElement().setAttribute("chunks", "" + chunksToSet); r.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { Widget w = (Widget) event.getSource(); chunks = Integer.valueOf(w.getElement().getAttribute("chunks")); getMemoryStats(chunks); getCpuStats(chunks); getExtraData(chunks); } }); return r; } /** * for extending popups to add additional elements to the panel */ public void addExtraElements(FlowPanel fp) { } public void loadSecondPart() { HorizontalPanel hp = new HorizontalPanel(); Button threadDump = new Button("Get Thread Dump"); hp.add(threadDump); addRadioButtons(hp); Button close = new Button("Close"); hp.add(close); close.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { hide(); } }); hp.setWidth("100%"); hp.setCellHorizontalAlignment(close, HorizontalAlignmentConstant.endOf(Direction.LTR)); Style style = close.getElement().getStyle(); style.setColor("orange"); style.setFontWeight(FontWeight.BOLDER); fp.add(hp); fp.add(cpu); fp.add(memory); fp.add(memoryDetails); fp.add(sysLoad); addExtraElements(fp); fp.add(details); threadDump.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { final ThreadDumpPopup tdp = new ThreadDumpPopup(); tdp.center(); service.getThreadDump(serverCode, new AsyncCallback<ThreadDump>() { @Override public void onSuccess(ThreadDump result) { tdp.setDump(result); } @Override public void onFailure(Throwable caught) { tdp.setText("Can't get thread dump:" + caught.getMessage()); } }); } }); getMemoryStats(chunks); getCpuStats(chunks); service.getRuntimeInfo(serverCode, new AsyncCallback<RuntimeInfo>() { @Override public void onSuccess(RuntimeInfo result) { updateRuntimeInfo(result); int left = (Window.getClientWidth() - getOffsetWidth()) >> 1; setPopupPosition(Math.max(Window.getScrollLeft() + left, 0), 26); } @Override public void onFailure(Throwable caught) { Log.error("error while getting server cpu stats:" + caught.getMessage()); } }); getExtraData(chunks); RepeatingCommand refreshCommand = new RepeatingCommand() { @Override public boolean execute() { if (refresh) { getMemoryStats(chunks); getCpuStats(chunks); getExtraData(chunks); Log.debug("Reschedule refresh"); } return refresh; } }; Scheduler.get().scheduleFixedDelay(refreshCommand, 20000); } @Override public void center() { setSize("760px", "450px"); super.center(); } public void getExtraData(Integer chunks) { } @Override public void hide() { super.hide(); refresh = false; nativePreviewHandler.removeHandler(); } private void getCpuStats(int chunks) { service.getCpuUsageHistory(serverCode, chunks, new AsyncCallback<LinkedList<CpuUtilizationChunk>>() { @Override public void onSuccess(LinkedList<CpuUtilizationChunk> result) { updateCpuChart(result); updateSysLoadChart(result); } @Override public void onFailure(Throwable caught) { Log.error("error while getting server cpu stats:" + caught.getMessage()); } }); } private void getMemoryStats(int chunks) { service.getMemoryStats(serverCode, chunks, new AsyncCallback<LinkedList<MemoryUsage>>() { @Override public void onSuccess(LinkedList<MemoryUsage> result) { memoryUsages = result; updateMemoryChart(result); updateMemoryDetailsChart(result); } @Override public void onFailure(Throwable caught) { Log.error("error while getting server memory stats:" + caught.getMessage()); }; }); } private void updateRuntimeInfo(RuntimeInfo rti) { FlexTable ft = new FlexTable(); ft.getElement().setId("infoTable"); ft.setCellPadding(0); ft.setCellSpacing(0); ft.setText(0, 0, "Name/Value"); ft.setText(0, 1, "Value"); int i = 1; ft.setText(i++, 0, "Name"); ft.setText(i++, 0, "BootClassPath"); ft.setText(i++, 0, "ClassPath"); ft.setText(i++, 0, "LibraryPath"); ft.setText(i++, 0, "AvailableProcessors"); ft.setText(i++, 0, "SystemLoadAverage"); ft.setText(i++, 0, "InputArguments"); ft.setText(i++, 0, "SystemProperties"); ft.getRowFormatter().getElement(0).setId("th"); i = 1; ft.setText(i++, 1, rti.getName()); createWrappedHTML(ft, i++, rti.getBootClassPath()); createWrappedHTML(ft, i++, rti.getClassPath()); createWrappedHTML(ft, i++, rti.getLibraryPath()); ft.setText(i++, 1, "" + rti.getAvailableProcessors()); ft.setText(i++, 1, "" + rti.getSystemLoadAverage()); TextArea ta = new TextArea(); StringBuilder sb = new StringBuilder(); for (String p : rti.getInputArguments()) { sb.append(p).append("\n"); } ta.setText(sb.toString()); ta.setSize("700px", "300px"); ft.setWidget(i++, 1, ta); ta = new TextArea(); sb = new StringBuilder(); for (String key : rti.getSystemProperties().keySet()) { sb.append(key + " = " + rti.getSystemProperties().get(key)).append("\n"); } ta.setText(sb.toString()); ta.setSize("700px", "300px"); ft.setWidget(i++, 1, ta); details.add(ft); } private void createWrappedHTML(FlexTable ft, int index, String text) { Widget h = getWrappedHtml(text); ft.setWidget(index, 1, h); ft.getCellFormatter().getElement(index, 1).setId("wrapContent"); } private Widget getWrappedHtml(String text) { TextArea ta = new TextArea(); ta.setText(text); ta.setSize("700px", "100px"); return ta; } private void updateCpuChart(LinkedList<CpuUtilizationChunk> percentList) { ChartFeed<Double, Long> cpuHistory = new ChartFeed<Double, Long>(new Double[1][percentList.size()], new Long[percentList.size()]); for (int k = 0; k < 2; k++) { for (int j = 0; j < percentList.size(); j++) { if (k == 0) { cpuHistory.getValues()[k][j] = percentList.get(j).getUsage(); } else if (k == 1) { cpuHistory.getXLineValues()[j] = percentList.get(j).getEndTime(); } } } Log.debug("ServerStatsPopup.Updating CPU, values size:" + cpuHistory.getValuesLenght()); cpuChart.updateChart(cpuHistory, true); } private void updateSysLoadChart(LinkedList<CpuUtilizationChunk> percentList) { ChartFeed<Double, Long> sysLoadFeed = new ChartFeed<Double, Long>(new Double[1][percentList.size()], new Long[percentList.size()]); for (int k = 0; k < 2; k++) { for (int j = 0; j < percentList.size(); j++) { if (k == 0) { sysLoadFeed.getValues()[k][j] = percentList.get(j).getSystemLoadAverage(); } else if (k == 1) { sysLoadFeed.getXLineValues()[j] = percentList.get(j).getEndTime(); } } } Log.debug("ServerStatsPopup.Updating SysLoad, values size:" + sysLoadFeed.getValuesLenght()); sysLoadChart.updateChart(sysLoadFeed, true); } private void updateMemoryChart(LinkedList<MemoryUsage> result) { if (result == null) { Log.warn("Empty result in memmory stats"); return; } ChartFeed<Double, Long> memoryHistory = new ChartFeed<Double, Long>(new Double[1][result.size()], new Long[result.size()]); for (int k = 0; k < 2; k++) { for (int j = 0; j < result.size(); j++) { if (k == 0) { memoryHistory.getValues()[k][j] = result.get(j).getPercentage(); } else if (k == 1) { memoryHistory.getXLineValues()[j] = result.get(j).getEndTime(); } } } Log.debug("ServerStatsPopup.Updating memry, values size:" + memoryHistory.getValuesLenght()); memoryChart.updateChart(memoryHistory, true); } private void updateMemoryDetailsChart(LinkedList<MemoryUsage> result) { if (result == null) { Log.warn("Empty result in memmory stats"); return; } HashMap<String, DynamicLine> names = new HashMap<String, DynamicLine>(0); int i = 0; for (MemoryUsage mu : result) { for (MemoryState memoryState : mu.getMemoryState()) { if (memoryState.isHeap() == showHeap && !names.containsKey(memoryState.getName())) { names.put(memoryState.getName(), new DynamicLine(i, memoryState.getName(), Colors.colors[i], ColumnType.NUMBER)); i++; } } } ChartFeed<Long, Long> memoryDetailsHistory = new ChartFeed<Long, Long>(new Long[names.size()][result.size()], new Long[result.size()]); for (int j = 0; j < result.size(); j++) { LinkedList<MemoryState> memoryState = result.get(j).getMemoryState(); for (MemoryState ms : memoryState) { if (names.containsKey(ms.getName())) { memoryDetailsHistory.getValues()[names.get(ms.getName()).getIndex()][j] = ms.getUsed(); } } memoryDetailsHistory.getXLineValues()[j] = result.get(j).getEndTime(); } Log.debug("ServerStatsPopup.Updating memry, values size:" + memoryDetailsHistory.getValuesLenght()); List<ILineType> ilt = new ArrayList<ILineType>(names.values()); memoryDetailsChart.updateChart(ilt, memoryDetailsHistory, true); } public Integer getServerCode() { return serverCode; } }