Mercurial > hg > release > thermostat-0.7
changeset 29:8b9113d4f428
add client gui
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/AgentRef.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,25 @@ +package com.redhat.thermostat.client; + +public class AgentRef { + + private final String uid; + private final String name; + + public AgentRef(String id, String name) { + this.uid = id; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public String getId() { + return uid; + } + + public String getName() { + return name; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ApplicationInfo.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,55 @@ +package com.redhat.thermostat.client; + +import java.util.Date; + +import javax.swing.Icon; + +import com.redhat.thermostat.client.ui.IconResource; + +public class ApplicationInfo { + + /* + * TODO All of these fields should be generated on build. + */ + + public String getName() { + return "thermostat"; + } + + public String getVersion() { + return "0.0.1"; + } + + public String getDescription() { + return "A monitoring and servicability tool for OpenJDK"; + } + + public Icon getIcon() { + return IconResource.QUESTION.getIcon(); + } + + public String getReleaseDate() { + return "2012-02-02"; + } + + public String getBuildDate() { + return new Date().toString(); + } + + public String getCopyright() { + return "(C) Copyright Red Hat, Inc"; + } + + public String getLicense() { + return "GPL2 + Classpath"; + } + + public String getEmail() { + return "an@example.com"; + } + + public String getWebsite() { + return "http://an.example.com"; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ClientArgs.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,51 @@ +package com.redhat.thermostat.client; + +import java.awt.Window; + +import com.redhat.thermostat.client.ui.LayoutDebugHelper; + +public class ClientArgs { + + private static boolean isDebugLayout = + Boolean.getBoolean("thermostat.debug-layout"); + + // private static boolean isDebugLayout = true; + + public ClientArgs(String[] initialArgs) { + // remove 'unused' warnings + for (String arg : initialArgs) { + if (arg.equals("--debug-layout")) { + isDebugLayout = true; + } + } + // TODO what arguments do we care about? + // perhaps skipping the mode selection? + + if (isDebugLayout()) { + Thread layoutDebugger = new Thread(new Runnable() { + @Override + public void run() { + LayoutDebugHelper helper = new LayoutDebugHelper(); + while (true) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + System.err.println("Layout Debug Helper exiting"); + } + Window[] windows = Window.getWindows(); + for (Window w : windows) { + helper.debugLayout(w); + w.invalidate(); + w.repaint(); + } + } + } + }); + layoutDebugger.start(); + } + } + + public static boolean isDebugLayout() { + return isDebugLayout; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ConnectionInfo.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,57 @@ +package com.redhat.thermostat.client; + +public class ConnectionInfo { + + public enum ConnectionType { + LOCAL(false), + REMOTE(true), + CLUSTER(true), + NONE(false, false), ; + + boolean isDisplayable = false; + boolean needsUrl = false; + + private ConnectionType(boolean needsUrl) { + this.needsUrl = needsUrl; + } + + private ConnectionType(boolean isDisplayable, boolean needsUrl) { + this.isDisplayable = isDisplayable; + } + + public boolean isDisplayable() { + return isDisplayable; + } + + public boolean needsUrl() { + return needsUrl; + } + } + + private ConnectionType type; + private String url; + + public void setType(ConnectionType type) { + this.type = type; + } + + public ConnectionType getType() { + return type; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + @Override + public String toString() { + if (url == null) { + return type.toString(); + } + return type.toString() + " to " + url; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/DummyFacade.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,258 @@ +package com.redhat.thermostat.client; + +import static com.redhat.thermostat.client.Translate._; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import com.redhat.thermostat.common.HostInfo; +import com.redhat.thermostat.common.NetworkInfo; +import com.redhat.thermostat.common.NetworkInterfaceInfo; +import com.redhat.thermostat.common.VmInfo; +import com.redhat.thermostat.common.VmMemoryStat; +import com.redhat.thermostat.common.VmMemoryStat.Generation; +import com.redhat.thermostat.common.VmMemoryStat.Space; + +public class DummyFacade implements ThermostatFacade, HostInformationFacade, VmInformationFacade { + + private final Random r = new Random(); + private List<MemoryType> toDisplay = new ArrayList<MemoryType>(); + + private AgentRef onlyAgent = new AgentRef("a-random-string-of-letters-and-numbers", "agent on localhost"); + private VmRef onlyVm = new VmRef(onlyAgent, "a-random-string-of-letters-and-numbers-or-perhaps-a-process-id", "super crazy awesome java app"); + + public DummyFacade() { + toDisplay.addAll(Arrays.asList(MemoryType.values())); + } + + @Override + public AgentRef[] getConnectedAgents() { + return new AgentRef[] { onlyAgent }; + } + + @Override + public VmRef[] getConnectedVms() { + return new VmRef[] { onlyVm }; + } + + @Override + public VmRef[] getVms() { + return new VmRef[] { onlyVm }; + } + + @Override + public HostInformationFacade getHost(AgentRef ref) { + return this; + } + + @Override + public HostInfo getHostInfo() { + String hostname = "host.example.com"; + String osName = "Fedora 99"; + String osKernel = "Linux 9.9.9.9"; + String cpuModel = "Some CPU @ some speed GHz"; + int cpuCount = 99; + long totalMemory = 1; + return new HostInfo(hostname, osName, osKernel, cpuModel, cpuCount, totalMemory); + } + + @Override + public NetworkInfo getNetworkInfo() { + NetworkInfo info = new NetworkInfo(); + + NetworkInterfaceInfo eth0 = new NetworkInterfaceInfo("eth0"); + eth0.setIp4Addr("1.1.1.1"); + eth0.setIp6Addr("1:::::::::1"); + info.addNetworkInterfaceInfo(eth0); + + NetworkInterfaceInfo em0 = new NetworkInterfaceInfo("em0"); + em0.setIp4Addr("256.256.256.256"); + info.addNetworkInterfaceInfo(em0); + + return info; + } + + @Override + public VmInformationFacade getVm(VmRef vmRef) { + return this; + } + + @Override + public VmInfo getVmInfo() { + + // TODO hook into storage and return the actual VmInfo object + int vmPid = 0; + long startTime = System.currentTimeMillis() - 10000; + long stopTime = Integer.MIN_VALUE; + String javaVersion = "2.9.9"; + String javaHome = "/usr/lib/jvm/java/jre/"; + String mainClass = "com.foo.bar"; + String commandLine = "some java program"; + String vmName = "hotspot usb compiler"; + String vmInfo = "future predictive mode"; + String vmVersion = "99b99"; + String vmArguments = "-XX:+EvenFasterPlease --X:+UNROLL_ALL_THE_LOOPS!"; + Map<String, String> properties = new HashMap<String, String>(); + Map<String, String> environment = new HashMap<String, String>(); + List<String> loadedNativeLibraries = new ArrayList<String>(); + return new VmInfo(vmPid, startTime, stopTime, javaVersion, javaHome, mainClass, commandLine, vmName, vmInfo, vmVersion, vmArguments, properties, environment, loadedNativeLibraries); + } + + @Override + public double[][] getCpuLoad() { + double[][] cpuData = new double[][] { + new double[] { 10000, r.nextDouble() }, + new double[] { 10010, r.nextDouble() }, + new double[] { 10020, r.nextDouble() }, + new double[] { 10030, r.nextDouble() }, + new double[] { 10040, r.nextDouble() }, + new double[] { 10050, r.nextDouble() }, + new double[] { 10060, r.nextDouble() }, + }; + + return cpuData; + } + + @Override + public long[][] getMemoryUsage(MemoryType type) { + long[][] data = new long[][] { + new long[] { 100010, r.nextLong() }, + new long[] { 100020, r.nextLong() }, + new long[] { 100030, r.nextLong() }, + new long[] { 100040, r.nextLong() }, + new long[] { 100050, r.nextLong() }, + new long[] { 100060, r.nextLong() }, + new long[] { 100070, r.nextLong() }, + new long[] { 100080, r.nextLong() }, + new long[] { 100090, r.nextLong() }, + new long[] { 100110, r.nextLong() }, + }; + return data; + } + + @Override + public MemoryType[] getMemoryTypesToDisplay() { + return toDisplay.toArray(new MemoryType[0]); + } + + @Override + public boolean isMemoryTypeDisplayed(MemoryType type) { + return toDisplay.contains(type); + } + + @Override + public void setDisplayMemoryType(MemoryType type, boolean selected) { + if (selected) { + if (!toDisplay.contains(type)) { + toDisplay.add(type); + } + } else { + toDisplay.remove(type); + } + + } + + @Override + public String[] getCollectorNames() { + return new String[] { "PSScavenge", "PSMarkSweep" }; + } + + @Override + public long getTotalInvocations() { + return 11; + } + + @Override + public long[][] getCollectorData(String collectorName) { + List<long[]> data = new ArrayList<long[]>(); + long last = 2; + data.add(new long[] { 100000, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100010, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100020, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100030, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100040, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100050, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100060, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100070, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100080, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100090, (last = last + r.nextInt(10)) }); + data.add(new long[] { 100110, (last = last + r.nextInt(10)) }); + + return data.toArray(new long[0][0]); + } + + @Override + public String getCollectorGeneration(String collectorName) { + if (collectorName.equals("PSScavenge")) { + return _("YOUNG_GEN"); + } else if (collectorName.equals("PSMarkSweep")) { + return _("OLD_GEN"); + } + return ("UNKNOWN_GEN"); + } + + @Override + public VmMemoryStat getMemoryInfo() { + long timestamp = -1; + List<Generation> generations = new ArrayList<Generation>(); + + Generation youngGen = new Generation(); + youngGen.name = "eden"; + generations.add(youngGen); + + List<Space> youngSpaces = new ArrayList<Space>(); + + Space eden = new Space(); + eden.name = _("EDEN_GEN"); + eden.used = 100; + eden.capacity = 200; + eden.maxCapacity = 300; + youngSpaces.add(eden); + + Space s0 = new Space(); + s0.name = _("S0_GEN"); + s0.used = 100; + s0.capacity = 200; + s0.maxCapacity = 400; + youngSpaces.add(s0); + + Space s1 = new Space(); + s1.name = _("S1_GEN"); + s1.used = 150; + s1.capacity = 200; + s1.maxCapacity = 400; + youngSpaces.add(s1); + + youngGen.spaces = youngSpaces; + + Generation oldGen = new Generation(); + generations.add(oldGen); + + Space oldSpace = new Space(); + oldSpace.name = _("OLD_GEN"); + oldSpace.used = 400; + oldSpace.capacity = 500; + oldSpace.maxCapacity = 600; + + oldGen.spaces = Arrays.asList(new Space[] { oldSpace }); + + Generation permGen = new Generation(); + generations.add(permGen); + + Space permSpace = new Space(); + permSpace.name = _("PERM_GEN"); + permSpace.used = 50; + permSpace.capacity = 200; + permSpace.maxCapacity = 200; + + permGen.spaces = Arrays.asList(new Space[] { permSpace }); + + VmMemoryStat stat = new VmMemoryStat(timestamp, 0, generations); + return stat; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/HostInformationFacade.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,25 @@ +package com.redhat.thermostat.client; + +import com.redhat.thermostat.common.HostInfo; +import com.redhat.thermostat.common.NetworkInfo; + +public interface HostInformationFacade { + public abstract VmRef[] getVms(); + + public abstract VmInformationFacade getVm(VmRef vmRef); + + public abstract HostInfo getHostInfo(); + + public abstract NetworkInfo getNetworkInfo(); + + public abstract double[][] getCpuLoad(); + + public abstract long[][] getMemoryUsage(MemoryType type); + + public abstract MemoryType[] getMemoryTypesToDisplay(); + + public abstract boolean isMemoryTypeDisplayed(MemoryType type); + + public abstract void setDisplayMemoryType(MemoryType type, boolean selected); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/Main.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,45 @@ +package com.redhat.thermostat.client; + +import javax.swing.JFrame; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.ConnectionInfo.ConnectionType; +import com.redhat.thermostat.client.ui.ConnectionSelectionDialog; +import com.redhat.thermostat.client.ui.MainWindow; + +public class Main { + + private static void showGui() { + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + + ConnectionInfo model = new ConnectionInfo(); + + ConnectionSelectionDialog dialog = new ConnectionSelectionDialog((JFrame) null, model); + dialog.pack(); + dialog.setModal(true); + dialog.setVisible(true); + + if (model.getType() == ConnectionType.NONE) { + return; + } + + ThermostatFacade facade = new DummyFacade(); + + MainWindow gui = new MainWindow(facade); + gui.setStartupMode(model.getType()); + gui.pack(); + gui.setVisible(true); + } + + public static void main(String[] args) { + ClientArgs arguments = new ClientArgs(args); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + showGui(); + } + }); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/MemoryType.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,28 @@ +package com.redhat.thermostat.client; + +import static com.redhat.thermostat.client.Translate._; + +public enum MemoryType { + MEMORY_TOTAL("total", _("HOST_MEMORY_TOTAL")), + MEMORY_FREE("free", _("HOST_MEMORY_FREE")), + MEMORY_USED("used", _("HOST_MEMORY_USED")), + SWAP_TOTAL("swap-total", _("HOST_SWAP_TOTAL")), + SWAP_FREE("swap-free", _("HOST_SWAP_FREE")), + SWAP_BUFFERS("swap-buffers", _("HOST_BUFFERS")); + + private String humanReadable; + + private MemoryType(String key, String humanReadable) { + this.humanReadable = humanReadable; + } + + public String getLabel() { + return humanReadable; + } + + @Override + public String toString() { + return humanReadable; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ThermostatFacade.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,10 @@ +package com.redhat.thermostat.client; + +public interface ThermostatFacade { + public abstract AgentRef[] getConnectedAgents(); + + public abstract VmRef[] getConnectedVms(); + + public abstract HostInformationFacade getHost(AgentRef ref); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/Translate.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,21 @@ +package com.redhat.thermostat.client; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +public class Translate { + + private static ResourceBundle resourceBundle = null; + + static { + resourceBundle = ResourceBundle.getBundle("com.redhat.thermostat.client.strings"); + } + + public static String _(String toTranslate) { + return resourceBundle.getString(toTranslate); + } + + public static String _(String toTranslate, String... params) { + return MessageFormat.format(_(toTranslate), (Object[]) params); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/VmInformationFacade.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,20 @@ +package com.redhat.thermostat.client; + +import com.redhat.thermostat.common.VmInfo; +import com.redhat.thermostat.common.VmMemoryStat; + +public interface VmInformationFacade { + + public VmInfo getVmInfo(); + + public String[] getCollectorNames(); + + public long getTotalInvocations(); + + public long[][] getCollectorData(String collectorName); + + public String getCollectorGeneration(String collectorName); + + public VmMemoryStat getMemoryInfo(); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/VmRef.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,27 @@ +package com.redhat.thermostat.client; + +public class VmRef { + + private final AgentRef agentRef; + private final String uid; + private final String name; + + public VmRef(AgentRef agentRef, String id, String name) { + this.agentRef = agentRef; + this.uid = id; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public String getId() { + return uid; + } + + public String getName() { + return name; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/strings.properties Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,116 @@ +MAIN_WINDOW_TITLE = Thermostat + +MAIN_WINDOW_TREE_ROOT_NAME = Thermostat + +WELCOME_MESSAGE = Welcome to thermostat!<br>\ + Thermostat is awesome++<br>\ + if you love it, contribute code!<br>\ + THAT IS ALL.<br><br>\ + A subtle thought that is in error may yet give rise to fruitful inquiry<br>\ + that can establish truths of great value<br>\ + -- Isaac Asimov<br> + +BUTTON_CLOSE = Close +BUTTON_NEXT = Next +BUTTON_CANCEL = Cancel +BUTTON_OK = OK + +MENU_FILE = File +MENU_FILE_CONNECT = Connect +MENU_FILE_IMPORT = Import +MENU_FILE_EXPORT = Export +MENU_FILE_EXIT = Exit +MENU_HELP = Help +MENU_HELP_ABOUT = About + +GARBAGE_COLLECTION = Garbage Collection +YOUNG_GEN = Young +EDEN_GEN = Eden +S0_GEN = Survivor 0 +S1_GEN = Survivor 1 +OLD_GEN = Tenured +PERM_GEN = Permanent +UNKNOWN_GEN = Unknown +SOME_GENERATION = {0} Generation + +STARTUP_MODE_SELECTION_DIALOG_TITLE = Welcome to Thermostat! +STARTUP_MODE_SELECTION_INTRO = Which JVMs to do you want to monitor? +STARTUP_MODE_SELECTION_TYPE_LOCAL = Local +STARTUP_MODE_SELECTION_TYPE_REMOTE = Remote +STARTUP_MODE_SELECTION_TYPE_CLUSTER = Cluster +STARTUP_MODE_SELECTION_URL_LABEL = Host Location + +ABOUT_DIALOG_VERSION_AND_RELEASE = Version {0} (released {1}) +ABOUT_DIALOG_LICENSE = Licensed under the {0} license. +ABOUT_DIALOG_EMAIL = Email: {0} +ABOUT_DIALOG_WEBSITE = Website: {0} + +HOME_PANEL_SECTION_SUMMARY = Summary +HOME_PANEL_TOTAL_MACHINES = Total Machines +HOME_PANEL_TOTAL_JVMS = Total Java Virtual Machines +HOME_PANEL_SECTION_ISSUES = Issues +HOME_PANEL_NO_ISSUES = No Issues + +HOST_INFO_TAB_OVERVIEW = Overview +HOST_INFO_TAB_MEMORY = Memory +HOST_INFO_TAB_CPU = Processor +HOST_INFO_TAB_IO = IO + +HOST_OVERVIEW_SECTION_BASICS = Basics +HOST_OVERVIEW_SECTION_HARDWARE = Hardware +HOST_OVERVIEW_SECTION_SOFTWARE = Software + +HOST_INFO_HOSTNAME = Host +HOST_INFO_CPU_COUNT = Processor Count +HOST_INFO_CPU_MODEL = Processor Model +HOST_INFO_OS_NAME = OS Name +HOST_INFO_OS_KERNEL = OS Kernel +HOST_INFO_MEMORY_TOTAL = Total Memory +HOST_INFO_NETWORK = Network +HOST_INFO_NETWORK_INTERFACE_ADDDRESS = {0} ({1} or {2}) + +HOST_CPU_SECTION_OVERVIEW = Processor +HOST_CPU_USAGE_CHART_TITLE = Cpu Usage +HOST_CPU_USAGE_CHART_TIME_LABEL = Time +HOST_CPU_USAGE_CHART_VALUE_LABEL = Usage + +HOST_MEMORY_SECTION_OVERVIEW = Memory +HOST_MEMORY_CHART_TITLE = Memory +HOST_MEMORY_CHART_TIME_LABEL = Time +HOST_MEMORY_CHART_SIZE_LABEL = Size (bytes) + +HOST_MEMORY_TOTAL = Total Memory +HOST_MEMORY_FREE = Free Memory +HOST_MEMORY_USED = Used Memory +HOST_SWAP_TOTAL = Total Swap +HOST_SWAP_FREE = Free Swap +HOST_BUFFERS = Buffers + +VM_INFO_TAB_OVERVIEW = Overview +VM_INFO_TAB_MEMORY = Memory +VM_INFO_TAB_GC = GC + +VM_INFO_SECTION_PROCESS = Process Information +VM_INFO_SECTION_JAVA = Java Information + +VM_INFO_PROCESS_ID = Process Id +VM_INFO_START_TIME = Start time +VM_INFO_STOP_TIME = Stop time +VM_INFO_MAIN_CLASS = Main Class +VM_INFO_COMMAND_LINE = Command Line +VM_INFO_JAVA_VERSION = Java Version +VM_INFO_VM = Virtual Machine +VM_INFO_VM_NAME_AND_VERSION = {0} version {1} +VM_INFO_PROPERTIES = Properties +VM_INFO_ENVIRONMENT = Environment +VM_INFO_LIBRARIES = Native Libraries + +VM_CURRENT_MEMORY_CHART_USED = Used +VM_CURRENT_MEMORY_CHART_CAPACITY = Capacity +VM_CURRENT_MEMORY_CHART_MAX_CAPACITY = Max Capacity +VM_CURRENT_MEMORY_CHART_SPACE = Memory Region +VM_CURRENT_MEMORY_CHART_SIZE = Size + +VM_GC_COLLECTOR_OVER_GENERATION = Collector {0} running on {1} +VM_GC_COLLECTOR_CHART_REAL_TIME_LABEL = Time +VM_GC_COLLECTOR_CHART_GC_TIME_LABEL = Total Time Spent on GC \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/AboutDialog.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,83 @@ +package com.redhat.thermostat.client.ui; + +import static com.redhat.thermostat.client.Translate._; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.Border; + +import com.redhat.thermostat.client.ApplicationInfo; + +public class AboutDialog extends JDialog { + + private static final long serialVersionUID = -2459715525658426058L; + + private static final Border emptySpace = BorderFactory.createEmptyBorder(10, 10, 10, 10); + + private final ApplicationInfo appInfo; + + public AboutDialog(ApplicationInfo info) { + this.appInfo = info; + setupUi(); + } + + private void setupUi() { + + String name = appInfo.getName(); + String description = appInfo.getDescription(); + String version = appInfo.getVersion(); + Icon icon = appInfo.getIcon(); + String releaseDate = appInfo.getReleaseDate(); + String buildDate = appInfo.getBuildDate(); + String copyright = appInfo.getCopyright(); + String license = appInfo.getLicense(); + String email = appInfo.getEmail(); + String website = appInfo.getWebsite(); + + JPanel iconContainer = new JPanel(new BorderLayout()); + JLabel iconLabel = new JLabel(icon); + iconLabel.setBorder(emptySpace); + iconContainer.add(iconLabel); + + add(iconContainer, BorderLayout.LINE_START); + + JPanel descriptionContainer = new JPanel(); + descriptionContainer.setLayout(new BoxLayout(descriptionContainer, BoxLayout.PAGE_AXIS)); + descriptionContainer.setBorder(emptySpace); + + descriptionContainer.add(Box.createGlue()); + descriptionContainer.add(new JLabel(new HtmlTextBuilder().larger(name).toHtml())); + descriptionContainer.add(new JLabel(_("ABOUT_DIALOG_VERSION_AND_RELEASE", version, releaseDate))); + descriptionContainer.add(new JLabel(description)); + descriptionContainer.add(new JLabel(copyright)); + descriptionContainer.add(new JLabel(_("ABOUT_DIALOG_LICENSE", license))); + descriptionContainer.add(new JLabel(_("ABOUT_DIALOG_EMAIL", email))); + JLabel websiteLink = new JLabel(_("ABOUT_DIALOG_WEBSITE", website)); + descriptionContainer.add(websiteLink); + descriptionContainer.add(Box.createGlue()); + + add(descriptionContainer, BorderLayout.CENTER); + + JPanel buttonContainer = new JPanel(); + JButton closeButton = new JButton(_("BUTTON_CLOSE")); + closeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + AboutDialog.this.dispose(); + } + }); + buttonContainer.add(closeButton); + add(buttonContainer, BorderLayout.PAGE_END); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/Components.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,30 @@ +package com.redhat.thermostat.client.ui; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.SwingConstants; +import javax.swing.border.Border; + +public class Components { + public static JLabel header(String text) { + JLabel label = new JLabel(HtmlTextBuilder.boldHtml(text)); + label.setHorizontalAlignment(SwingConstants.LEADING); + return label; + } + + public static JLabel label(String string) { + JLabel label = new JLabel(string); + label.setHorizontalAlignment(SwingConstants.TRAILING); + return label; + } + + public static JLabel value(String value) { + JLabel toDisplay = new JLabel(value); + toDisplay.setHorizontalAlignment(SwingConstants.LEADING); + return toDisplay; + } + + public static Border smallBorder() { + return BorderFactory.createEmptyBorder(5, 5, 5, 5); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,134 @@ +package com.redhat.thermostat.client.ui; + +import static com.redhat.thermostat.client.Translate._; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import com.redhat.thermostat.client.ConnectionInfo; +import com.redhat.thermostat.client.ConnectionInfo.ConnectionType; + +public class ConnectionSelectionDialog extends JDialog { + + private static final long serialVersionUID = -3149845673473434408L; + + private static final int ICON_LABEL_GAP = 5; + + private final ConnectionInfo model; + + public ConnectionSelectionDialog(JFrame owner, ConnectionInfo model) { + super(owner); + setTitle(_("STARTUP_MODE_SELECTION_DIALOG_TITLE")); + this.model = model; + setupUi(); + } + + private void setupUi() { + BorderLayout layout = new BorderLayout(); + setLayout(layout); + add(createModeSelectionUi(), BorderLayout.CENTER); + + FlowLayout bottomPanelLayout = new FlowLayout(FlowLayout.TRAILING); + JPanel bottomPanel = new JPanel(bottomPanelLayout); + add(bottomPanel, BorderLayout.PAGE_END); + bottomPanel.add(Box.createGlue()); + + JPanel buttonsPanel = new JPanel(new GridLayout(1, 5, ICON_LABEL_GAP, ICON_LABEL_GAP)); + buttonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + bottomPanel.add(buttonsPanel); + + JButton cancelButton = new JButton(_("BUTTON_CANCEL")); + cancelButton.setMargin(new Insets(0, 15, 0, 15)); + cancelButton.addActionListener(new SetStartupModeListener(this, ConnectionType.NONE)); + buttonsPanel.add(cancelButton); + } + + private JPanel createModeSelectionUi() { + JPanel container = new JPanel(); + container.setBorder(BorderFactory.createEmptyBorder(ICON_LABEL_GAP, ICON_LABEL_GAP, ICON_LABEL_GAP, ICON_LABEL_GAP)); + GridBagLayout layout = new GridBagLayout(); + container.setLayout(layout); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + Insets normalInsets = new Insets(ICON_LABEL_GAP, ICON_LABEL_GAP, ICON_LABEL_GAP, ICON_LABEL_GAP); + c.insets = normalInsets; + c.gridy++; + c.gridx = 0; + c.gridwidth = GridBagConstraints.REMAINDER; + c.weighty = 0; + + JLabel info = new JLabel(_("STARTUP_MODE_SELECTION_INTRO")); + container.add(info, c); + + c.gridy++; + String localButtonHtml = buildHtml(_("STARTUP_MODE_SELECTION_TYPE_LOCAL"), IconResource.COMPUTER.getUrl()); + JButton localButton = new JButton(localButtonHtml); + container.add(localButton, c); + + c.gridy++; + String remoteButtonHtml = buildHtml(_("STARTUP_MODE_SELECTION_TYPE_REMOTE"), IconResource.NETWORK_SERVER.getUrl()); + JButton remoteButton = new JButton(remoteButtonHtml); + container.add(remoteButton, c); + + c.gridy++; + String clusterButtonHtml = buildHtml(_("STARTUP_MODE_SELECTION_TYPE_CLUSTER"), IconResource.NETWORK_GROUP.getUrl()); + JButton clusterButton = new JButton(clusterButtonHtml); + container.add(clusterButton, c); + + localButton.addActionListener(new SetStartupModeListener(this, ConnectionType.LOCAL)); + remoteButton.addActionListener(new SetStartupModeListener(this, ConnectionType.REMOTE)); + clusterButton.addActionListener(new SetStartupModeListener(this, ConnectionType.CLUSTER)); + return container; + } + + private String buildHtml(String text, String imageUrl) { + /* build a table to vertically align image and text properly */ + // TODO does not deal correctly with right-to-left languages + String html = "" + + "<html>" + + " <table>" + + " <tr> " + + " <td> " + "<img src='" + imageUrl + "'>" + "</td>" + + " <td>" + new HtmlTextBuilder().huge(text).toHtml() + "</td>" + + " </tr>" + + " </table>" + + "</html>"; + return html; + } + + public ConnectionInfo getModel() { + return model; + } + + private static class SetStartupModeListener implements ActionListener { + private final ConnectionType mode; + private final ConnectionSelectionDialog window; + + public SetStartupModeListener(ConnectionSelectionDialog frame, ConnectionType mode) { + this.mode = mode; + this.window = frame; + } + + @Override + public void actionPerformed(ActionEvent e) { + window.getModel().setType(mode); + window.setVisible(false); + window.dispose(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/HomePanel.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,105 @@ +package com.redhat.thermostat.client.ui; + +import static com.redhat.thermostat.client.Translate._; + +import java.awt.BorderLayout; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractListModel; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListModel; + +import com.redhat.thermostat.client.ThermostatFacade; +import com.redhat.thermostat.client.ui.SimpleTable.Section; +import com.redhat.thermostat.client.ui.SimpleTable.TableEntry; + +public class HomePanel extends JPanel { + + private static final long serialVersionUID = -5953027789947771737L; + + private final ThermostatFacade facade; + + public HomePanel(ThermostatFacade facade) { + this.facade = facade; + + setLayout(new BorderLayout()); + + JPanel intro = new JPanel(); + intro.setLayout(new BorderLayout()); + JLabel homeIcon = new JLabel(IconResource.USER_HOME.getIcon()); + intro.add(homeIcon, BorderLayout.LINE_START); + + JLabel welcomeMessageLabel = new JLabel(new HtmlTextBuilder(_("WELCOME_MESSAGE")).toHtml()); + intro.add(welcomeMessageLabel, BorderLayout.CENTER); + + add(intro, BorderLayout.PAGE_START); + + List<Section> sections = new ArrayList<Section>(); + TableEntry entry; + + Section summarySection = new Section(_("HOME_PANEL_SECTION_SUMMARY")); + sections.add(summarySection); + + entry = new TableEntry(_("HOME_PANEL_TOTAL_MACHINES"), String.valueOf(facade.getConnectedAgents().length)); + summarySection.add(entry); + entry = new TableEntry(_("HOME_PANEL_TOTAL_JVMS"), String.valueOf(facade.getConnectedVms().length)); + summarySection.add(entry); + + JPanel summaryPanel = SimpleTable.createTable(sections); + summaryPanel.setBorder(Components.smallBorder()); + add(summaryPanel, BorderLayout.CENTER); + + JPanel issuesPanel = createIssuesPanel(); + issuesPanel.setBorder(Components.smallBorder()); + add(issuesPanel, BorderLayout.PAGE_END); + + } + + public JPanel createIssuesPanel() { + JPanel result = new JPanel(new BorderLayout()); + + result.add(Components.header(_("HOME_PANEL_SECTION_ISSUES")), BorderLayout.PAGE_START); + + ListModel model = new IssuesListModel(new ArrayList<Object>()); + + JList issuesList = new JList(model); + result.add(new JScrollPane(issuesList), BorderLayout.CENTER); + + return result; + } + + private static class IssuesListModel extends AbstractListModel { + + private static final long serialVersionUID = 7131506292620902850L; + + private List<? extends Object> delegate; + + private String emptyElement = new String(_("HOME_PANEL_NO_ISSUES")); + + public IssuesListModel(List<? extends Object> actualList) { + this.delegate = actualList; + // TODO observe the delegate for changes + } + + @Override + public int getSize() { + if (delegate.isEmpty()) { + return 1; + } + return delegate.size(); + } + + @Override + public Object getElementAt(int index) { + if (delegate.isEmpty()) { + return emptyElement; + } + return delegate.get(index); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/HostPanel.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,269 @@ +package com.redhat.thermostat.client.ui; + +import static com.redhat.thermostat.client.Translate._; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.swing.AbstractButton; +import javax.swing.JCheckBox; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import com.redhat.thermostat.client.HostInformationFacade; +import com.redhat.thermostat.client.MemoryType; +import com.redhat.thermostat.client.ui.SimpleTable.Key; +import com.redhat.thermostat.client.ui.SimpleTable.Section; +import com.redhat.thermostat.client.ui.SimpleTable.TableEntry; +import com.redhat.thermostat.client.ui.SimpleTable.Value; +import com.redhat.thermostat.common.HostInfo; +import com.redhat.thermostat.common.NetworkInterfaceInfo; + +public class HostPanel extends JPanel { + + /* + * This entire class needs to be more dynamic. We should try to avoid + * creating objects and should just update them when necessary + */ + + private static final long serialVersionUID = 4835316442841009133L; + + private final HostInformationFacade facade; + private final HostInfo hostInfo; + + public HostPanel(HostInformationFacade facade) { + this.facade = facade; + this.hostInfo = facade.getHostInfo(); + init(); + } + + private void init() { + setLayout(new BorderLayout()); + + JTabbedPane tabPane = new JTabbedPane(); + + tabPane.insertTab(_("HOST_INFO_TAB_OVERVIEW"), null, createOverviewPanel(), null, 0); + tabPane.insertTab(_("HOST_INFO_TAB_CPU"), null, createCpuStatisticsPanel(), null, 1); + tabPane.insertTab(_("HOST_INFO_TAB_MEMORY"), null, createMemoryStatisticsPanel(), null, 2); + + // TODO additional tabs provided by plugins + // tabPane.insertTab(title, icon, component, tip, 3) + + this.add(tabPane); + + } + + private JPanel createOverviewPanel() { + + TableEntry entry; + List<Section> allSections = new ArrayList<Section>(); + + Section basics = new Section(_("HOST_OVERVIEW_SECTION_BASICS")); + allSections.add(basics); + + entry = new TableEntry(_("HOST_INFO_HOSTNAME"), hostInfo.getHostname()); + basics.add(entry); + + Section hardware = new Section(_("HOST_OVERVIEW_SECTION_HARDWARE")); + allSections.add(hardware); + + entry = new TableEntry(_("HOST_INFO_CPU_MODEL"), hostInfo.getCpuModel()); + hardware.add(entry); + entry = new TableEntry(_("HOST_INFO_CPU_COUNT"), String.valueOf(hostInfo.getCpuCount())); + hardware.add(entry); + entry = new TableEntry(_("HOST_INFO_MEMORY_TOTAL"), String.valueOf(hostInfo.getTotalMemory())); + hardware.add(entry); + + Key key = new Key(_("HOST_INFO_NETWORK")); + List<Value> values = new ArrayList<Value>(); + for (Iterator<NetworkInterfaceInfo> iter = facade.getNetworkInfo().getInterfacesIterator(); iter.hasNext();) { + NetworkInterfaceInfo networkInfo = iter.next(); + String ifaceName = networkInfo.getInterfaceName(); + String ipv4 = networkInfo.getIp4Addr(); + String ipv6 = networkInfo.getIp6Addr(); + values.add(new Value(_("HOST_INFO_NETWORK_INTERFACE_ADDDRESS", ifaceName, ipv4, ipv6))); + } + hardware.add(new TableEntry(key, values)); + + Section software = new Section(_("HOST_OVERVIEW_SECTION_SOFTWARE")); + allSections.add(software); + + entry = new TableEntry(_("HOST_INFO_OS_NAME"), hostInfo.getOsName()); + software.add(entry); + entry = new TableEntry(_("HOST_INFO_OS_KERNEL"), hostInfo.getOsKernel()); + software.add(entry); + + JPanel table = SimpleTable.createTable(allSections); + table.setBorder(Components.smallBorder()); + return table; + } + + private JPanel createCpuStatisticsPanel() { + + JPanel contentArea = new JPanel(); + contentArea.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.FIRST_LINE_START; + c.fill = GridBagConstraints.NONE; + c.gridx = 0; + c.gridy = 0; + + List<Section> allSections = new ArrayList<Section>(); + + Section cpuBasics = new Section(_("HOST_CPU_SECTION_OVERVIEW")); + allSections.add(cpuBasics); + + TableEntry entry; + entry = new TableEntry(_("HOST_INFO_CPU_MODEL"), hostInfo.getCpuModel()); + cpuBasics.add(entry); + entry = new TableEntry(_("HOST_INFO_CPU_COUNT"), String.valueOf(hostInfo.getCpuCount())); + cpuBasics.add(entry); + + JPanel table = SimpleTable.createTable(allSections); + table.setBorder(Components.smallBorder()); + contentArea.add(table, c); + + double[][] cpuData = facade.getCpuLoad(); + XYSeries series = new XYSeries("cpu-load"); + for (double[] data : cpuData) { + series.add(data[0], data[1]); + } + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + JFreeChart chart = ChartFactory.createTimeSeriesChart( + _("HOST_CPU_USAGE_CHART_TITLE"), + _("HOST_CPU_USAGE_CHART_TIME_LABEL"), + _("HOST_CPU_USAGE_CHART_VALUE_LABEL"), + dataset, + false, false, false); + + ChartPanel chartPanel = new ChartPanel(chart); + // make this chart non-interactive + chartPanel.setDisplayToolTips(true); + chartPanel.setDoubleBuffered(true); + chartPanel.setMouseZoomable(false); + chartPanel.setPopupMenu(null); + + c.gridy++; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + + contentArea.add(chartPanel, c); + return contentArea; + } + + private JPanel createMemoryStatisticsPanel() { + JPanel contentArea = new JPanel(); + // contentArea.setLayout(new GridBagLayout()); + contentArea.setLayout(new BorderLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.FIRST_LINE_START; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 0; + c.gridy = 0; + + List<Section> allSections = new ArrayList<Section>(); + + Section memoryBasics = new Section(_("HOST_MEMORY_SECTION_OVERVIEW")); + allSections.add(memoryBasics); + + TableEntry entry; + entry = new TableEntry(_("HOST_INFO_MEMORY_TOTAL"), String.valueOf(hostInfo.getTotalMemory())); + memoryBasics.add(entry); + + + JPanel table = SimpleTable.createTable(allSections); + table.setBorder(Components.smallBorder()); + contentArea.add(table, BorderLayout.PAGE_START); + + JFreeChart chart = createMemoryChart(facade); + + ChartPanel chartPanel = new ChartPanel(chart); + // make this chart non-interactive + chartPanel.setDisplayToolTips(true); + chartPanel.setDoubleBuffered(true); + chartPanel.setMouseZoomable(false); + chartPanel.setPopupMenu(null); + + c.gridy++; + c.fill = GridBagConstraints.BOTH; + c.gridwidth = GridBagConstraints.REMAINDER; + c.weightx = 1; + c.weighty = 1; + contentArea.add(chartPanel, BorderLayout.CENTER); + + JPanel memoryPanel = new JPanel(new WrapLayout(FlowLayout.LEADING)); + contentArea.add(memoryPanel, BorderLayout.PAGE_END); + + for (MemoryType type : MemoryType.values()) { + JCheckBox checkBox = new JCheckBox(type.getLabel(), facade.isMemoryTypeDisplayed(type)); + checkBox.addActionListener(new UpdateMemoryGraph(facade, chartPanel, type)); + memoryPanel.add(checkBox); + } + + return contentArea; + } + + private static JFreeChart createMemoryChart(HostInformationFacade facade) { + XYSeriesCollection dataset = new XYSeriesCollection(); + + // FIXME associate a fixed color with each type + + for (MemoryType type : facade.getMemoryTypesToDisplay()) { + XYSeries series = new XYSeries(type.name()); + long[][] data = facade.getMemoryUsage(type); + for (long[] point : data) { + series.add(point[0], point[1]); + } + dataset.addSeries(series); + } + + JFreeChart chart = ChartFactory.createTimeSeriesChart( + _("HOST_MEMORY_CHART_TITLE"), // Title + _("HOST_MEMORY_CHART_TIME_LABEL"), // x-axis Label + _("HOST_MEMORY_CHART_SIZE_LABEL"), // y-axis Label + dataset, // Dataset + false, // Show Legend + false, // Use tooltips + false // Configure chart to generate URLs? + ); + return chart; + } + + private static class UpdateMemoryGraph implements ActionListener { + + private final HostInformationFacade facade; + private final MemoryType type; + private final ChartPanel chartPanel; + + public UpdateMemoryGraph(HostInformationFacade facade, ChartPanel chartPanel, MemoryType type) { + this.facade = facade; + this.chartPanel = chartPanel; + this.type = type; + } + + @Override + public void actionPerformed(ActionEvent e) { + AbstractButton abstractButton = (AbstractButton) e.getSource(); + boolean selected = abstractButton.getModel().isSelected(); + facade.setDisplayMemoryType(type, selected); + chartPanel.setChart(createMemoryChart(facade)); + } + + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/HtmlTextBuilder.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,72 @@ +package com.redhat.thermostat.client.ui; + +public class HtmlTextBuilder { + + /* + * The api provided by this class needs to be cleaned up. + */ + + private final StringBuilder text = new StringBuilder(); + + public HtmlTextBuilder() { + // do nothing + } + + public HtmlTextBuilder(String text) { + text = escape(text); + this.text.append(text); + } + + public HtmlTextBuilder bold(boolean on) { + if (on) { + this.text.append("<b>"); + } else { + this.text.append("</b>"); + } + return this; + } + + public HtmlTextBuilder bold(String toBold) { + text.append("<b>").append(toBold).append("</b>"); + return this; + } + + public HtmlTextBuilder larger(String toAppend) { + text.append("<font size='+2'>").append(escape(toAppend)).append("</font>"); + return this; + } + + public HtmlTextBuilder huge(String toAppend) { + text.append("<font size='+6'>").append(escape(toAppend)).append("</font>"); + return this; + } + + @Override + public String toString() { + // FIXME + return null; + } + + public String toHtml() { + return "<html>" + text.toString() + "</html>"; + } + + public String toPartialHtml() { + return text.toString(); + } + + private static String escape(String text) { + // FIXME implement this + return text; + } + + public HtmlTextBuilder append(String value) { + text.append(value); + return this; + } + + public static String boldHtml(String toBold) { + return new HtmlTextBuilder().bold(toBold).toHtml(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/IconResource.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,49 @@ +package com.redhat.thermostat.client.ui; + +import java.io.File; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +public class IconResource { + /* FIXME we need to pick up the icons dynamically */ + + private static final String ICON_PREFIX = "/usr/share/icons/gnome/"; + + // an icon that should always be available and indicate that the actual icon + // is missing. + public static final IconResource MISSING_ICON = null; + + public static final IconResource ERROR = new IconResource(ICON_PREFIX + "48x48/status/dialog-error.png"); + public static final IconResource QUESTION = new IconResource(ICON_PREFIX + "256x256/status/dialog-question.png"); + public static final IconResource WARNING = new IconResource(ICON_PREFIX + "48x48/status/dialog-warning.png"); + + public static final IconResource COMPUTER = new IconResource(ICON_PREFIX + "48x48/devices/computer.png"); + public static final IconResource NETWORK_SERVER = new IconResource(ICON_PREFIX + "48x48/places/network-server.png"); + public static final IconResource NETWORK_GROUP = new IconResource(ICON_PREFIX + "48x48/places/network-workgroup.png"); + + public static final IconResource SEARCH = new IconResource(ICON_PREFIX + "16x16/actions/search.png"); + + public static final IconResource USER_HOME = new IconResource(ICON_PREFIX + "256x256/places/user-home.png"); + + private final String path; + + private IconResource(String descriptor) { + this.path = descriptor; + } + + public Icon getIcon() { + if (new File(path).exists()) { + return new ImageIcon(path); + } + return null; + } + + public String getPath() { + return path; + } + + public String getUrl() { + return "file:" + getPath(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/LayoutDebugHelper.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,43 @@ +package com.redhat.thermostat.client.ui; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Window; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; + +/** + * This class goes through the swing container hierarchy and adds random colored + * borders to the components themselves. it makes it easier to see what the + * {@code LayoutManager}s are doing + */ +public class LayoutDebugHelper { + + private Color[] colors = new Color[] { Color.BLACK, Color.BLUE, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.PINK, Color.ORANGE, Color.RED, Color.YELLOW }; + private int colorIndex = 0; + + public void debugLayout(Window w) { + Component[] children = w.getComponents(); + debugLayout(children); + } + + public void debugLayout(Component c) { + if (c instanceof JComponent) { + JComponent panel = (JComponent) c; + try { + panel.setBorder(BorderFactory.createLineBorder(colors[colorIndex % colors.length])); + } catch (IllegalArgumentException iae) { + // never mind then + } + colorIndex++; + debugLayout(panel.getComponents()); + } + } + + public void debugLayout(Component[] components) { + for (Component aComponent : components) { + debugLayout(aComponent); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/MainWindow.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,327 @@ +package com.redhat.thermostat.client.ui; + +import static com.redhat.thermostat.client.Translate._; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JLayeredPane; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JSplitPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.ScrollPaneConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.plaf.synth.SynthConstants; +import javax.swing.plaf.synth.SynthContext; +import javax.swing.plaf.synth.SynthLookAndFeel; +import javax.swing.plaf.synth.SynthStyle; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +import com.redhat.thermostat.client.AgentRef; +import com.redhat.thermostat.client.ThermostatFacade; +import com.redhat.thermostat.client.ApplicationInfo; +import com.redhat.thermostat.client.ClientArgs; +import com.redhat.thermostat.client.ConnectionInfo.ConnectionType; +import com.redhat.thermostat.client.VmRef; + +public class MainWindow extends JFrame { + + private static final long serialVersionUID = 5608972421496808177L; + + private final DefaultMutableTreeNode root = new DefaultMutableTreeNode(_("MAIN_WINDOW_TREE_ROOT_NAME")); + private final DefaultTreeModel treeModel = new DefaultTreeModel(root); + + private final ThermostatFacade facade; + + private JPanel contentArea = null; + private JTree agentVmTree = null; + private JTextField searchField = null; + + public MainWindow(ThermostatFacade facade) { + super(); + setTitle(_("MAIN_WINDOW_TITLE")); + + this.facade = facade; + + searchField = new JTextField(); + agentVmTree = new AgentVmTree(treeModel); + contentArea = new VerticalOnlyScrollingPanel(); + contentArea.setLayout(new BorderLayout()); + + setupMenus(); + setupPanels(); + + agentVmTree.setSelectionPath(new TreePath(root.getPath())); + + buildTree(""); + + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + } + + private void setupMenus() { + JMenuBar mainMenuBar = new JMenuBar(); + + JMenu fileMenu = new JMenu(_("MENU_FILE")); + fileMenu.getPopupMenu().setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); + mainMenuBar.add(fileMenu); + + JMenuItem fileConnectMenu = new JMenuItem(_("MENU_FILE_CONNECT")); + fileConnectMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // TODO present a connection dialog + } + }); + fileMenu.add(fileConnectMenu); + + fileMenu.add(new Separator()); + + JMenuItem fileImportMenu = new JMenuItem(_("MENU_FILE_IMPORT")); + fileMenu.add(fileImportMenu); + + JMenuItem fileExportMenu = new JMenuItem(_("MENU_FILE_EXPORT")); + fileMenu.add(fileExportMenu); + + fileMenu.add(new Separator()); + + JMenuItem fileExitMenu = new JMenuItem(_("MENU_FILE_EXIT")); + fileExitMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_DOWN_MASK)); + fileExitMenu.addActionListener(new ShtudownClient(this)); + fileMenu.add(fileExitMenu); + + JMenu helpMenu = new JMenu(_("MENU_HELP")); + helpMenu.getPopupMenu().setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); + mainMenuBar.add(helpMenu); + + JMenuItem helpAboutMenu = new JMenuItem(_("MENU_HELP_ABOUT")); + helpAboutMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + AboutDialog aboutDialog = new AboutDialog(new ApplicationInfo()); + aboutDialog.setModal(true); + aboutDialog.pack(); + aboutDialog.setVisible(true); + } + }); + helpMenu.add(helpAboutMenu); + setJMenuBar(mainMenuBar); + } + + private void setupPanels() { + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + + JPanel navigationPanel = new JPanel(new BorderLayout()); + + JPanel searchPanel = new JPanel(new BorderLayout()); + + navigationPanel.add(searchPanel, BorderLayout.PAGE_START); + + /* the insets are so we can place the actual icon inside the searchField */ + searchField.setMargin(new Insets(0, 0, 0, 30)); + searchField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent event) { + changed(event.getDocument()); + } + + @Override + public void insertUpdate(DocumentEvent event) { + changed(event.getDocument()); + } + + @Override + public void changedUpdate(DocumentEvent event) { + changed(event.getDocument()); + } + + private void changed(Document doc) { + String filter = null; + try { + filter = doc.getText(0, doc.getLength()); + } catch (BadLocationException ble) { + // ignore + } + buildTree(filter); + } + }); + + searchPanel.add(searchField); + // TODO move this icon inside the search field + JLabel searchIcon = new JLabel(IconResource.SEARCH.getIcon()); + searchIcon.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + searchPanel.add(searchIcon, BorderLayout.LINE_END); + + agentVmTree.addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent e) { + if (e.isAddedPath()) { + contentArea.removeAll(); + TreePath path = e.getPath(); + if (path.getPathCount() == 1) {/* root */ + contentArea.add(new HomePanel(facade)); + } else if (path.getPathCount() == 2) { /* agent */ + AgentRef agentRef = (AgentRef) ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject(); + HostPanel panel = new HostPanel(facade.getHost(agentRef)); + contentArea.add(panel); + } else { /* vm */ + AgentRef agentRef = (AgentRef) ((DefaultMutableTreeNode) path.getParentPath().getLastPathComponent()).getUserObject(); + VmRef vmRef = (VmRef) ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject(); + VmPanel panel = new VmPanel(facade.getHost(agentRef).getVm(vmRef)); + contentArea.add(panel); + } + // Fixes some 'ghosting' caused by the previous components + // to stay painted on the JViewPort + ((JScrollPane) contentArea.getParent().getParent()).repaint(); + contentArea.revalidate(); + } + } + + }); + + JScrollPane treeScrollPane = new JScrollPane(agentVmTree); + + navigationPanel.add(treeScrollPane); + + JPanel detailsPanel = createDetailsPanel(); + + splitPane.add(navigationPanel); + splitPane.add(detailsPanel); + + add(splitPane); + } + + private JPanel createDetailsPanel() { + JPanel result = new JPanel(new BorderLayout()); + if (ClientArgs.isDebugLayout()) { + contentArea.setBorder(BorderFactory.createLineBorder(Color.GREEN)); + } + JScrollPane contentScrollPane = new JScrollPane(contentArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + result.add(contentScrollPane, BorderLayout.CENTER); + if (ClientArgs.isDebugLayout()) { + result.setBorder(BorderFactory.createLineBorder(Color.PINK)); + } + return result; + } + + public void setStartupMode(ConnectionType type) { + // TODO use type to set up connection + } + + private void buildTree(String filter) { + root.removeAllChildren(); + treeModel.setRoot(null); + // paths to expand. only expand paths when a vm matches (to ensure it is + // visible) + List<TreeNode[]> pathsToExpand = new ArrayList<TreeNode[]>(); + if (filter == null || filter.trim().equals("")) { + DefaultMutableTreeNode agentNode; + AgentRef[] agentRefs = facade.getConnectedAgents(); + for (AgentRef agentRef : agentRefs) { + agentNode = new DefaultMutableTreeNode(agentRef); + root.add(agentNode); + VmRef[] vmRefs = facade.getHost(agentRef).getVms(); + for (VmRef vmRef : vmRefs) { + agentNode.add(new DefaultMutableTreeNode(vmRef)); + } + } + treeModel.setRoot(root); + } else { + DefaultMutableTreeNode agentNode; + for (AgentRef agentRef : facade.getConnectedAgents()) { + if (agentRef.getName().contains(filter) || agentRef.getId().contains(filter)) { + agentNode = new DefaultMutableTreeNode(agentRef); + root.add(agentNode); + VmRef[] vmRefs = facade.getHost(agentRef).getVms(); + for (VmRef vmRef : vmRefs) { + agentNode.add(new DefaultMutableTreeNode(vmRef)); + } + } else { + agentNode = null; + for (VmRef vmRef : facade.getHost(agentRef).getVms()) { + if (vmRef.getName().contains(filter) || vmRef.getId().contains(filter)) { + if (agentNode == null) { + agentNode = new DefaultMutableTreeNode(agentRef); + root.add(agentNode); + } + DefaultMutableTreeNode vmNode = new DefaultMutableTreeNode(vmRef); + agentNode.add(vmNode); + pathsToExpand.add(vmNode.getPath()); + } + } + } + } + if (root.getChildCount() > 0) { + treeModel.setRoot(root); + } + + } + for (TreeNode[] path : pathsToExpand) { + agentVmTree.expandPath(new TreePath(path).getParentPath()); + } + agentVmTree.expandRow(0); + } + + public static class ShtudownClient implements ActionListener { + + private JFrame toDispose; + + public ShtudownClient(JFrame toDispose) { + this.toDispose = toDispose; + } + + @Override + public void actionPerformed(ActionEvent e) { + toDispose.dispose(); + } + } + + private static class AgentVmTree extends JTree { + private static final long serialVersionUID = 8894141735861100579L; + + public AgentVmTree(TreeModel model) { + super(model); + getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + } + } + + private static class Separator extends JPopupMenu.Separator { + @Override + public Dimension getPreferredSize() { + Dimension result = super.getPreferredSize(); + if (result.height < 1) { + result.height = 5; + } + return result; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/SimpleTable.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,151 @@ +package com.redhat.thermostat.client.ui; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.Box; +import javax.swing.JPanel; + +public class SimpleTable { + + public static class Section { + private final String sectionName; + private final List<TableEntry> tableEntries = new ArrayList<TableEntry>(); + + public Section(String name) { + this.sectionName = name; + } + + public String getText() { + return sectionName; + } + + public void add(TableEntry entry) { + tableEntries.add(entry); + } + + public void add(Key key, List<Value> values) { + tableEntries.add(new TableEntry(key, values)); + } + + public void add(Key key, Value value) { + tableEntries.add(new TableEntry(key, value)); + } + + public TableEntry[] getEntries() { + return tableEntries.toArray(new TableEntry[0]); + } + } + + public static class TableEntry { + private final Key key; + private final List<Value> values; + + public TableEntry(String key, String value) { + this(new Key(key), new Value(value)); + } + + public TableEntry(Key key, Value value) { + this.key = key; + this.values = new ArrayList<Value>(); + this.values.add(value); + } + + public TableEntry(Key key, List<Value> values) { + this.key = key; + this.values = new ArrayList<Value>(values); + } + + public Key getKey() { + return key; + } + + public Value[] getValues() { + return values.toArray(new Value[0]); + } + + } + + public static class Key { + private final String text; + + public Key(String text) { + this.text = text; + } + + public String getText() { + return text; + } + } + + public static class Value { + private final String text; + + public Value(String text) { + this.text = text; + } + + public String getText() { + return text; + } + } + + public static JPanel createTable(List<Section> sections) { + final int SECTION_TOP_GAP = 10; + final int ROW_VERTICAL_GAP = 0; + final int ROW_HORIZONTAL_GAP = 10; + + Insets sectionHeaderInsets = new Insets(SECTION_TOP_GAP, 0, 0, 0); + Insets rowInsets = new Insets(ROW_VERTICAL_GAP, ROW_HORIZONTAL_GAP, ROW_VERTICAL_GAP, ROW_HORIZONTAL_GAP); + + JPanel container = new JPanel(); + container.setLayout(new GridBagLayout()); + + GridBagConstraints keyConstraints = new GridBagConstraints(); + GridBagConstraints valueConstraints = new GridBagConstraints(); + GridBagConstraints sectionHeaderConstraints = new GridBagConstraints(); + + keyConstraints.insets = valueConstraints.insets = rowInsets; + keyConstraints.gridy = valueConstraints.gridy = 0; + keyConstraints.gridx = 0; + valueConstraints.gridx = 1; + keyConstraints.fill = valueConstraints.fill = GridBagConstraints.HORIZONTAL; + + sectionHeaderConstraints.gridx = 0; + sectionHeaderConstraints.gridwidth = GridBagConstraints.REMAINDER; + sectionHeaderConstraints.fill = GridBagConstraints.HORIZONTAL; + sectionHeaderConstraints.insets = sectionHeaderInsets; + + for (Section section : sections) { + sectionHeaderConstraints.gridy = keyConstraints.gridy = ++valueConstraints.gridy; + container.add(Components.header(section.getText()), sectionHeaderConstraints); + for (TableEntry tableEntry : section.getEntries()) { + keyConstraints.gridy = ++valueConstraints.gridy; + container.add(Components.label(tableEntry.getKey().getText()), keyConstraints); + + for (Value value : tableEntry.getValues()) { + container.add(Components.value(value.getText()), valueConstraints); + keyConstraints.gridy = ++valueConstraints.gridy; + } + } + } + + GridBagConstraints glueConstraints = new GridBagConstraints(); + glueConstraints.gridy = keyConstraints.gridy + 1; + glueConstraints.gridx = 0; + glueConstraints.weightx = 1; + glueConstraints.weighty = 1; + glueConstraints.fill = GridBagConstraints.BOTH; + glueConstraints.gridheight = GridBagConstraints.REMAINDER; + glueConstraints.gridwidth = GridBagConstraints.REMAINDER; + Component filler = Box.createGlue(); + container.add(filler, glueConstraints); + + return container; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/VerticalOnlyScrollingPanel.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,52 @@ +package com.redhat.thermostat.client.ui; + +import java.awt.Dimension; +import java.awt.Rectangle; + +import javax.swing.JPanel; +import javax.swing.JViewport; +import javax.swing.Scrollable; + +/** + * A JPanel, that when added to a JScrollPane shows allows vertical scrolling. + */ +public class VerticalOnlyScrollingPanel extends JPanel implements Scrollable { + + private static final long serialVersionUID = -1039658895594239585L; + + public VerticalOnlyScrollingPanel() { + super(); + } + + @Override + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + // FIXME + return 5; + } + + @Override + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + // FIXME + return 100; + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override + public boolean getScrollableTracksViewportHeight() { + if (getParent() instanceof JViewport) { + JViewport viewport = (JViewport) getParent(); + return getPreferredSize().getHeight() < viewport.getHeight(); + } + return false; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/VmPanel.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,207 @@ +package com.redhat.thermostat.client.ui; + +import static com.redhat.thermostat.client.Translate._; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.data.category.DefaultCategoryDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import com.redhat.thermostat.client.VmInformationFacade; +import com.redhat.thermostat.client.ui.SimpleTable.Section; +import com.redhat.thermostat.client.ui.SimpleTable.TableEntry; +import com.redhat.thermostat.common.VmInfo; +import com.redhat.thermostat.common.VmMemoryStat; +import com.redhat.thermostat.common.VmMemoryStat.Generation; +import com.redhat.thermostat.common.VmMemoryStat.Space; + +public class VmPanel extends JPanel { + + private static final long serialVersionUID = 2816226547554943368L; + + private final VmInformationFacade facade; + + private final VmInfo vmInfo; + + public VmPanel(VmInformationFacade facade) { + this.facade = facade; + this.vmInfo = facade.getVmInfo(); + createUI(); + } + + public void createUI() { + setLayout(new BorderLayout()); + + JTabbedPane tabPane = new JTabbedPane(); + + tabPane.insertTab(_("VM_INFO_TAB_OVERVIEW"), null, createOverviewPanel(), null, 0); + tabPane.insertTab(_("VM_INFO_TAB_MEMORY"), null, createMemoryPanel(), null, 1); + tabPane.insertTab(_("VM_INFO_TAB_GC"), null, createGcPanel(), _("GARBAGE_COLLECTION"), 2); + + // TODO additional tabs provided by plugins + // tabPane.insertTab(title, icon, component, tip, 3) + + this.add(tabPane); + } + + public JPanel createOverviewPanel() { + JPanel panel = new JPanel(); + panel.setBorder(Components.smallBorder()); + panel.setLayout(new BorderLayout()); + + TableEntry entry; + List<Section> allSections = new ArrayList<Section>(); + + Section processSection = new Section(_("VM_INFO_SECTION_PROCESS")); + allSections.add(processSection); + + entry = new TableEntry(_("VM_INFO_PROCESS_ID"), String.valueOf(vmInfo.getVmPid())); + processSection.add(entry); + entry = new TableEntry(_("VM_INFO_START_TIME"), String.valueOf(vmInfo.getStartTimeStamp())); + processSection.add(entry); + entry = new TableEntry(_("VM_INFO_STOP_TIME"), String.valueOf(vmInfo.getStopTimeStamp())); + processSection.add(entry); + + Section javaSection = new Section(_("VM_INFO_SECTION_JAVA")); + allSections.add(javaSection); + + entry = new TableEntry(_("VM_INFO_MAIN_CLASS"), vmInfo.getMainClass()); + javaSection.add(entry); + entry = new TableEntry(_("VM_INFO_COMMAND_LINE"), vmInfo.getJavaCommandLine()); + javaSection.add(entry); + entry = new TableEntry(_("VM_INFO_JAVA_VERSION"), vmInfo.getJavaVersion()); + javaSection.add(entry); + entry = new TableEntry(_("VM_INFO_VM"), _("VM_INFO_VM_NAME_AND_VERSION", vmInfo.getVmName(), vmInfo.getVmVersion())); + javaSection.add(entry); + + JPanel table = SimpleTable.createTable(allSections); + table.setBorder(Components.smallBorder()); + panel.add(table, BorderLayout.PAGE_START); + + return panel; + } + + private Component createMemoryPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + panel.add(createCurrentMemoryDisplay(), c); + c.gridy++; + panel.add(createMemoryHistoryPanel(), c); + return panel; + } + + private Component createCurrentMemoryDisplay() { + DefaultCategoryDataset data = new DefaultCategoryDataset(); + + VmMemoryStat info = facade.getMemoryInfo(); + List<Generation> generations = info.getGenerations(); + for (Generation generation : generations) { + List<Space> spaces = generation.spaces; + for (Space space : spaces) { + data.addValue(space.used, _("VM_CURRENT_MEMORY_CHART_USED"), space.name); + data.addValue(space.capacity - space.used, _("VM_CURRENT_MEMORY_CHART_CAPACITY"), space.name); + data.addValue(space.maxCapacity - space.capacity, _("VM_CURRENT_MEMORY_CHART_MAX_CAPACITY"), space.name); + } + } + + JFreeChart chart = ChartFactory.createStackedBarChart( + null, + _("VM_CURRENT_MEMORY_CHART_SPACE"), + _("VM_CURRENT_MEMORY_CHART_SIZE"), + data, + PlotOrientation.HORIZONTAL, true, false, false); + + ChartPanel chartPanel = new ChartPanel(chart); + // make this chart non-interactive + chartPanel.setDisplayToolTips(true); + chartPanel.setDoubleBuffered(true); + chartPanel.setMouseZoomable(false); + chartPanel.setPopupMenu(null); + + return chartPanel; + } + + private Component createMemoryHistoryPanel() { + JPanel historyPanel = new JPanel(); + + return historyPanel; + } + + private Component createGcPanel() { + JPanel gcPanel = new JPanel(); + gcPanel.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + + String[] collectorNames = facade.getCollectorNames(); + for (int i = 0; i < collectorNames.length; i++) { + String collectorName = collectorNames[i]; + gcPanel.add(createCollectorDetailsPanel(collectorName), c); + c.gridy++; + } + + return gcPanel; + } + + private Component createCollectorDetailsPanel(String collectorName) { + JPanel detailsPanel = new JPanel(); + detailsPanel.setBorder(Components.smallBorder()); + detailsPanel.setLayout(new BorderLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.fill = GridBagConstraints.BOTH; + + detailsPanel.add(Components.header(_("VM_GC_COLLECTOR_OVER_GENERATION", collectorName, facade.getCollectorGeneration(collectorName))), BorderLayout.NORTH); + + long[][] cpuData = facade.getCollectorData(collectorName); + XYSeries series = new XYSeries("gc-runs"); + for (long[] data : cpuData) { + series.add(data[0], data[1]); + } + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + JFreeChart chart = ChartFactory.createTimeSeriesChart( + null, + _("VM_GC_COLLECTOR_CHART_REAL_TIME_LABEL"), + _("VM_GC_COLLECTOR_CHART_GC_TIME_LABEL"), + dataset, + false, false, false); + + ChartPanel chartPanel = new ChartPanel(chart); + // make this chart non-interactive + chartPanel.setDisplayToolTips(true); + chartPanel.setDoubleBuffered(true); + chartPanel.setMouseZoomable(false); + chartPanel.setPopupMenu(null); + + detailsPanel.add(chartPanel, BorderLayout.CENTER); + + return detailsPanel; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/redhat/thermostat/client/ui/WrapLayout.java Mon Jan 09 18:27:54 2012 -0500 @@ -0,0 +1,193 @@ +/* + * Taken from http://tips4java.wordpress.com/2008/11/06/wrap-layout/ + * + * The about page (http://tips4java.wordpress.com/about/) says this: + * "You are free to use and/or modify any or all code posted on the Java Tips + * Weblog without restriction. A credit in the code comments would be nice, + * but not in any way mandatory." + */ + +package com.redhat.thermostat.client.ui; + +import java.awt.*; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +/** + * FlowLayout subclass that fully supports wrapping of components. + */ +public class WrapLayout extends FlowLayout +{ + private Dimension preferredLayoutSize; + + /** + * Constructs a new <code>WrapLayout</code> with a left + * alignment and a default 5-unit horizontal and vertical gap. + */ + public WrapLayout() + { + super(); + } + + /** + * Constructs a new <code>FlowLayout</code> with the specified + * alignment and a default 5-unit horizontal and vertical gap. + * The value of the alignment argument must be one of + * <code>WrapLayout</code>, <code>WrapLayout</code>, + * or <code>WrapLayout</code>. + * @param align the alignment value + */ + public WrapLayout(int align) + { + super(align); + } + + /** + * Creates a new flow layout manager with the indicated alignment + * and the indicated horizontal and vertical gaps. + * <p> + * The value of the alignment argument must be one of + * <code>WrapLayout</code>, <code>WrapLayout</code>, + * or <code>WrapLayout</code>. + * @param align the alignment value + * @param hgap the horizontal gap between components + * @param vgap the vertical gap between components + */ + public WrapLayout(int align, int hgap, int vgap) + { + super(align, hgap, vgap); + } + + /** + * Returns the preferred dimensions for this layout given the + * <i>visible</i> components in the specified target container. + * @param target the component which needs to be laid out + * @return the preferred dimensions to lay out the + * subcomponents of the specified container + */ + @Override + public Dimension preferredLayoutSize(Container target) + { + return layoutSize(target, true); + } + + /** + * Returns the minimum dimensions needed to layout the <i>visible</i> + * components contained in the specified target container. + * @param target the component which needs to be laid out + * @return the minimum dimensions to lay out the + * subcomponents of the specified container + */ + @Override + public Dimension minimumLayoutSize(Container target) + { + Dimension minimum = layoutSize(target, false); + minimum.width -= (getHgap() + 1); + return minimum; + } + + /** + * Returns the minimum or preferred dimension needed to layout the target + * container. + * + * @param target target to get layout size for + * @param preferred should preferred size be calculated + * @return the dimension to layout the target container + */ + private Dimension layoutSize(Container target, boolean preferred) + { + synchronized (target.getTreeLock()) + { + // Each row must fit with the width allocated to the containter. + // When the container width = 0, the preferred width of the container + // has not yet been calculated so lets ask for the maximum. + + int targetWidth = target.getSize().width; + + if (targetWidth == 0) + targetWidth = Integer.MAX_VALUE; + + int hgap = getHgap(); + int vgap = getVgap(); + Insets insets = target.getInsets(); + int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); + int maxWidth = targetWidth - horizontalInsetsAndGap; + + // Fit components into the allowed width + + Dimension dim = new Dimension(0, 0); + int rowWidth = 0; + int rowHeight = 0; + + int nmembers = target.getComponentCount(); + + for (int i = 0; i < nmembers; i++) + { + Component m = target.getComponent(i); + + if (m.isVisible()) + { + Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); + + // Can't add the component to current row. Start a new row. + + if (rowWidth + d.width > maxWidth) + { + addRow(dim, rowWidth, rowHeight); + rowWidth = 0; + rowHeight = 0; + } + + // Add a horizontal gap for all components after the first + + if (rowWidth != 0) + { + rowWidth += hgap; + } + + rowWidth += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + } + + addRow(dim, rowWidth, rowHeight); + + dim.width += horizontalInsetsAndGap; + dim.height += insets.top + insets.bottom + vgap * 2; + + // When using a scroll pane or the DecoratedLookAndFeel we need to + // make sure the preferred size is less than the size of the + // target containter so shrinking the container size works + // correctly. Removing the horizontal gap is an easy way to do this. + + Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); + + if (scrollPane != null) + { + dim.width -= (hgap + 1); + } + + return dim; + } + } + + /* + * A new row has been completed. Use the dimensions of this row + * to update the preferred size for the container. + * + * @param dim update the width and height when appropriate + * @param rowWidth the width of the row to add + * @param rowHeight the height of the row to add + */ + private void addRow(Dimension dim, int rowWidth, int rowHeight) + { + dim.width = Math.max(dim.width, rowWidth); + + if (dim.height > 0) + { + dim.height += getVgap(); + } + + dim.height += rowHeight; + } +}