/** * 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.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.clevermore.monitor.client.ServerWidgetService; import org.clevermore.monitor.client.ServerWidgetServiceAsync; import org.clevermore.monitor.client.utils.ClientStringFormatter; import org.clevermore.monitor.client.utils.LocalStorage; import org.clevermore.monitor.client.widgets.AbstractMonitoringWidget; import org.clevermore.monitor.client.widgets.IMonitoringWidget; import org.clevermore.monitor.shared.certificate.Certificate; import org.clevermore.monitor.shared.config.ClientConfigurations; import org.clevermore.monitor.shared.servers.ConnectedDB; import org.clevermore.monitor.shared.servers.ConnectedServer; import org.clevermore.monitor.shared.servers.ServersRefreshRequest; import org.clevermore.monitor.shared.servers.ServersRefreshResponse; 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.Style; import com.google.gwt.dom.client.Style.Cursor; import com.google.gwt.dom.client.Style.FontWeight; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.event.dom.client.MouseOverHandler; import com.google.gwt.user.client.Window; 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.FlexTable; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; public class ServersWidget<CC extends ClientConfigurations> extends AbstractMonitoringWidget<ServersRefreshRequest, ServersRefreshResponse, ServerWidgetServiceAsync> implements IMonitoringWidget { private static final String SERVERS_SHOW_OFF = "servers.showOff"; private static final String SERVERS_FILTER = "servers.filter"; private FlowPanel serversList = new FlowPanel(); private ScrollPanel sp = new ScrollPanel(); private boolean showOffline = true; private Button certs = new Button("Certificates"); private ClickHandler getThreadDump = new ClickHandler() { @Override public void onClick(ClickEvent event) { String code = ((Widget) event.getSource()).getElement().getAttribute("code"); if (code != null) { ServerStatsPopup<CC> ssp = new ServerStatsPopup<CC>(Integer.valueOf(code)); ssp.center(); } else { Window.alert("Server couldn't be found:" + code); } } }; private ClickHandler getGCHistory = new ClickHandler() { @Override public void onClick(ClickEvent event) { String code = ((Widget) event.getSource()).getElement().getAttribute("code"); getService().getGCHistory(Integer.valueOf(code), new AsyncCallback<String>() { @Override public void onSuccess(String result) { GcHistoryPopup ghp = new GcHistoryPopup(); ghp.setText(result); ghp.center(); } @Override public void onFailure(Throwable caught) { Window.alert("Can't get GC history:" + caught.getMessage()); } }); } }; private MouseOverHandler handCursor = new MouseOverHandler() { @Override public void onMouseOver(MouseOverEvent event) { ((Widget) event.getSource()).getElement().getStyle().setCursor(Cursor.POINTER); } }; private Map<Integer, ConnectedServer> serversMap = new HashMap<Integer, ConnectedServer>(0); private ArrayList<ConnectedServer> servesList; private ArrayList<ConnectedDB> databases; private HorizontalPanel title = new HorizontalPanel(); private TextBox filter = new TextBox(); private Label serversLabel = new Label("Servers"); public ServersWidget() { super("Servers", 20000, (ServerWidgetServiceAsync) GWT.create(ServerWidgetService.class)); addStyleName("serversWidget"); serversList.setStyleName("serversWidgetInternal"); getDataPanel().add(serversList); sp.setSize("100%", "100%"); serversList.add(sp); title.setStyleName("serversHeader"); setTitleWidget(title); title.add(serversLabel); final CheckBox chkShowOffline = new CheckBox("Show Offline"); chkShowOffline.setValue(true); title.add(chkShowOffline); title.add(new Label("Filter:")); title.add(filter); Style style = certs.getElement().getStyle(); style.setPadding(0, Unit.PX); title.add(certs); title.add(getRefProg()); certs.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { getService().getCertificates(new AsyncCallback<HashMap<String, List<Certificate>>>() { @Override public void onSuccess(HashMap<String, List<Certificate>> result) { new CertificatesPopup(result); } @Override public void onFailure(Throwable caught) { Window.alert(caught.getMessage()); } }); } }); String filterText = LocalStorage.readStoredItem(SERVERS_FILTER); if (filterText != null) { filter.setText(filterText.trim().toLowerCase()); } filter.addKeyPressHandler(new KeyPressHandler() { @Override public void onKeyPress(KeyPressEvent event) { Log.debug("Key:" + event.getNativeEvent().getKeyCode()); if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { updateServersTable(); } } }); String showOff = LocalStorage.readStoredItem(SERVERS_SHOW_OFF); if (showOff != null && (showOff.equals("1") || showOff.equals("0"))) { chkShowOffline.setValue(showOff.equals("1")); showOffline = showOff.equals("1"); } chkShowOffline.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { showOffline = chkShowOffline.getValue(); LocalStorage.storeItem(SERVERS_SHOW_OFF, showOffline ? "1" : "0"); } }); } @Override public void clear() { } private void updateServersTable() { sp.clear(); FlexTable ft = new FlexTable(); sp.add(ft); ft.getElement().setId("infoTable"); ft.setCellPadding(0); ft.setCellSpacing(0); Collections.sort(servesList, new Comparator<ConnectedServer>() { @Override public int compare(ConnectedServer o1, ConnectedServer o2) { double o1p = 0, o2p = 0; if (o1.getStatus() && o2.getStatus()) { if (o1.getCpuUsage() > 80 || o2.getCpuUsage() > 80) { // sort by CPU o1p = o1.getCpuUsage(); o2p = o2.getCpuUsage(); } else { // sort by memory o1p = o1.getMemoryUsage().getPercentage(); o2p = o2.getMemoryUsage().getPercentage(); } } else if (o1.getStatus()) { o1p = o1.getMemoryUsage().getPercentage(); o2p = 100; } else if (o2.getStatus()) { o1p = 100; o2p = o2.getMemoryUsage().getPercentage(); } return (int) (o2p * 100d - o1p * 100d); } }); int i = 0, j = 0; HTML t; ft.setText(i, j++, "Code, Name"); ft.setText(i, j++, "Up Time"); t = new HTML("Memory"); t.setTitle("Latest memory status."); ft.setWidget(i, j++, t); t = new HTML("Max GC"); t.setTitle("Max GC time in the last 24 hours."); ft.setWidget(i, j++, t); t = new HTML("Cpu"); t.setTitle("Latest CPU load."); ft.setWidget(i, j++, t); t = new HTML("SYSL"); t.setTitle("Linux system load."); ft.setWidget(i, j++, t); ft.getRowFormatter().getElement(i++).setId("th"); int offline = 0; serversMap.clear(); for (ConnectedServer cs : servesList) { serversMap.put(cs.getServerCode(), cs); j = 0; offline += cs.getStatus() ? 0 : 1; if (!toShow(cs.getName())) { continue; } if (cs.getStatus()) { final HTML name = new HTML("<a href=#>" + cs.getServerCode() + "," + cs.getName() + "</a>"); name.getElement().setAttribute("code", "" + cs.getServerCode()); name.setTitle(cs.getToolTip()); name.addMouseOverHandler(handCursor); name.addClickHandler(getThreadDump); ft.setWidget(i, j++, name); ft.setText(i, j++, ClientStringFormatter.formatMilisecondsToHours(cs.getUpTime())); HTML usage = new HTML(ClientStringFormatter.formatMBytes(cs.getMemoryUsage().getUsed()) + " / " + ClientStringFormatter.formatMBytes(cs.getMemoryUsage().getMax()) + " MB, " + ClientStringFormatter.formatMillisShort(cs.getMemoryUsage().getPercentage()) + "%"); ft.setWidget(i, j++, usage); StringBuilder gcs = new StringBuilder(); double gcMax = Double.MIN_VALUE; // Iterating over all available pools for (Double gch : cs.getGcHistories()) { gcs.append(ClientStringFormatter.formatMillisShort(gch)).append(", "); if (gch > gcMax) { gcMax = gch; } } if (gcs.length() > 0) { gcs.setLength(gcs.length() - 2); } final HTML memory = new HTML(gcs.toString()); memory.setTitle("Click to see GC history"); memory.addMouseOverHandler(handCursor); memory.addClickHandler(getGCHistory); memory.getElement().setAttribute("code", "" + cs.getServerCode()); ft.setWidget(i, j++, memory); if (gcMax > 3) { Style style = ft.getFlexCellFormatter().getElement(i, j - 1).getStyle(); style.setBackgroundColor("#C00000"); style.setFontWeight(FontWeight.BOLDER); style.setColor("white"); } if (cs.getMemoryUsage().getPercentage() > 90) { ft.getRowFormatter().getElement(i).setId("memoryVeryHigh"); } // else if (cs.getMemoryUsage().getPercentage() > 80) { // ft.getRowFormatter().getElement(i).setId("memoryHigh"); // } HTML cpu = new HTML(cs.getCpuUsage() + "%"); ft.setWidget(i, j++, cpu); if (cs.getCpuUsage() > 90d) { Style style = ft.getFlexCellFormatter().getElement(i, j - 1).getStyle(); style.setBackgroundColor("#C00000"); style.setFontWeight(FontWeight.BOLDER); style.setColor("white"); } double sla = cs.getSystemLoadAverage(); HTML sysl = new HTML(sla == -1 ? "N/A" : ClientStringFormatter.formatMillisShort(sla)); sysl.setTitle("System Load Average"); ft.setWidget(i, j++, sysl); i++; } else { if (showOffline) { final HTML name = new HTML("<a href=#>" + cs.getServerCode() + ", " + cs.getName() + "</a>"); name.setTitle("JMX >> " + cs.getIp() + ":" + cs.getJmxPort()); ft.setWidget(i, j++, name); ft.setText(i, j++, "Offline"); ft.setText(i, j++, "Offline"); ft.setText(i, j++, "Offline"); ft.setText(i, j++, "0.0%"); ft.setText(i, j++, "0"); ft.getRowFormatter().getElement(i++).setId("offline"); } } } ft.getColumnFormatter().setWidth(0, "100px"); if (databases != null && databases.size() > 0) { for (ConnectedDB cd : databases) { j = 0; HTML value = new HTML("<b>Database</b>:" + cd.getName() + " (" + cd.getService() + ")"); value.setTitle(cd.toString()); ft.setWidget(i, j, value); ft.getFlexCellFormatter().setColSpan(i, j, 2); ft.setText(i, 1, cd.getIp() + ":" + cd.getPort()); ft.setText(i, 2, (cd.getStatus() ? "ONLINE" : "OFFLINE")); ft.setText(i, 3, "LastPing:" + cd.getLastPingTime() + "ms"); ft.getFlexCellFormatter().setColSpan(i, 3, 2); if ((!cd.getStatus())) { ft.getRowFormatter().getElement(i).setId("offline"); } i++; } } serversLabel.setText("Servers:" + servesList.size() + " (" + offline + ")"); } private boolean toShow(String serverName) { filter.setText(filter.getText().trim().toLowerCase().replace(",", " ")); if (filter.getText().length() > 0) { // filter LocalStorage.storeItem(SERVERS_FILTER, filter.getText()); filter.getElement().getStyle().setBackgroundColor("#66FF00"); String[] fil = filter.getText().split(" "); for (String f : fil) { if (f.trim().length() > 0 && serverName.trim().toLowerCase().contains(f)) { return true; } } return false; } else { filter.getElement().getStyle().setBackgroundColor("white"); LocalStorage.removeItem(SERVERS_FILTER); return true; } } @Override public ServersRefreshRequest createRefreshRequest() { return new ServersRefreshRequest(); } @Override public void refreshFailed(Throwable t) { Log.error("ServerWidget, error refereshing:" + t.getMessage(), t); } @Override public void refresh(ServersRefreshResponse refershResponse) { Log.debug("ServerWidget, refereshing:" + refershResponse.toString()); Log.debug("ServersWidget spInterrupted:" + sp.getVerticalScrollPosition()); servesList = refershResponse.getServers(); databases = refershResponse.getDatabases(); updateServersTable(); getRefProg().progress(); if (refershResponse.isCertificateAlert()) { blinkCertButton(); } else { stopBlinkCertButton(); } } private void blinkCertButton() { Boolean blink = Boolean.valueOf(certs.getElement().getAttribute("blink")); if (!blink) { certs.getElement().setAttribute("blink", "true"); Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { @Override public boolean execute() { Boolean blink = Boolean.valueOf(certs.getElement().getAttribute("blink")); Style style = certs.getElement().getStyle(); if (blink) { style.setColor("red".equals(style.getColor()) ? "green" : "red"); style.setFontWeight(FontWeight.BOLDER); } else { style.setColor("black"); style.setFontWeight(FontWeight.NORMAL); } return blink; } }, 1000); } } private void stopBlinkCertButton() { certs.getElement().setAttribute("blink", "false"); } }