Mercurial > hg > release > thermostat-0.13
changeset 220:22d3c37b94a0
UI to enable/disable the agent backend from the client
Reviewed-by: rkennke
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-April/000720.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/AgentConfigurationSource.java Thu Apr 05 15:45:55 2012 -0400 @@ -0,0 +1,63 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AgentConfigurationSource { + + // FIXME fix this properly + + public List<String> getKnownAgents() { + return Arrays.asList(new String[] { "Agent Smith", "Agent Jones", "Agent Brown" }); + } + + public Map<String, Boolean> getAgentBackends(String agentName) { + Map<String, Boolean> fake = new HashMap<>(); + fake.put("Monitor New JVMs", true); + fake.put("Use up all my CPU Cycles", false); + return fake; + } + + public void updateAgentConfig(String agentName, Map<String, Boolean> newBackendStatus) { + // TODO Auto-generated method stub + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/Configuration.java Thu Apr 05 15:45:55 2012 -0400 @@ -0,0 +1,65 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client; + +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.ui.AgentConfigurationController; +import com.redhat.thermostat.client.ui.AgentConfigurationFrame; +import com.redhat.thermostat.client.ui.AgentConfigurationModel; +import com.redhat.thermostat.client.ui.AgentConfigurationView; + +public class Configuration { + + public void showAgentConfiguration() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + AgentConfigurationView view = new AgentConfigurationFrame(); + AgentConfigurationSource agentPrefs = new AgentConfigurationSource(); + AgentConfigurationModel model = new AgentConfigurationModel(agentPrefs); + AgentConfigurationController controller = new AgentConfigurationController(model, view); + controller.showView(); + } + }); + } + + public static void main(String[] args) { + new Configuration().showAgentConfiguration(); + } + +}
--- a/client/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java Thu Apr 12 10:38:24 2012 -0400 +++ b/client/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java Thu Apr 05 15:45:55 2012 -0400 @@ -55,6 +55,8 @@ MENU_FILE_IMPORT, MENU_FILE_EXPORT, MENU_FILE_EXIT, + MENU_EDIT, + MENU_EDIT_CONFIGURE_AGENT, MENU_HELP, MENU_HELP_ABOUT, @@ -172,7 +174,11 @@ VM_LOADED_CLASSES, VM_CLASSES_CHART_REAL_TIME_LABEL, - VM_CLASSES_CHART_LOADED_CLASSES_LABEL; + VM_CLASSES_CHART_LOADED_CLASSES_LABEL, + + CONFIGURE_AGENT_WINDOW_TITLE, + CONFIGURE_AGENT_AGENTS_LIST, + CONFIGURE_ENABLE_BACKENDS; static final String RESOURCE_BUNDLE = "com.redhat.thermostat.client.locale.strings";
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationController.java Thu Apr 05 15:45:55 2012 -0400 @@ -0,0 +1,116 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import com.redhat.thermostat.client.ui.AgentConfigurationView.ConfigurationAction; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; + +public class AgentConfigurationController implements ActionListener<ConfigurationAction> { + + private final AgentConfigurationView view; + private final AgentConfigurationModel model; + private String agentName = null; + + public AgentConfigurationController(AgentConfigurationModel model, AgentConfigurationView view) { + this.view = view; + this.model = model; + + view.addActionListener(this); + + } + + public void showView() { + Collection<String> agents = model.getAgents(); + agentName = null; + for (String agentName: agents) { + if (this.agentName == null) { + this.agentName = agentName; + } + view.addAgent(agentName); + } + view.showDialog(); + updateViewFromModel(); + } + + public void hideView() { + view.hideDialog(); + } + + @Override + public void actionPerformed(ActionEvent<ConfigurationAction> actionEvent) { + switch (actionEvent.getActionId()) { + case SWITCH_AGENT: + updateModelFromCurrentView(); + String agentName = view.getSelectedAgent(); + this.agentName = agentName; + updateViewFromModel(); + break; + case CLOSE_ACCEPT: + updateModelFromCurrentView(); + model.saveConfiguration(); + /* fall through */ + case CLOSE_CANCEL: + hideView(); + break; + default: + throw new IllegalArgumentException("unknown event"); + } + } + + private void updateModelFromCurrentView() { + Map<String, Boolean> map = view.getBackendStatus(); + for (Entry<String, Boolean> entry: map.entrySet()) { + model.setBackendEnabled(agentName, entry.getKey(), entry.getValue()); + } + + } + + private void updateViewFromModel() { + Map<String, Boolean> map = new HashMap<>(); + for (String backendName: model.getBackends(agentName)) { + map.put(backendName, model.getAgentBackendEnabled(agentName, backendName)); + } + view.setBackendStatus(map); + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationFrame.java Thu Apr 05 15:45:55 2012 -0400 @@ -0,0 +1,306 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import static com.redhat.thermostat.client.locale.Translate.localize; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.swing.Box; +import javax.swing.DefaultListModel; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.LayoutStyle.ComponentPlacement; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.locale.LocaleResources; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import javax.swing.JScrollPane; +import javax.swing.JList; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +public class AgentConfigurationFrame extends JFrame implements AgentConfigurationView, java.awt.event.ActionListener, ListSelectionListener { + + private final CopyOnWriteArrayList<ActionListener<ConfigurationAction>> listeners = new CopyOnWriteArrayList<>(); + + private final Map<String, JCheckBox> backends = Collections.synchronizedMap(new HashMap<String, JCheckBox>()); + + private final JPanel availableBackendsPanel; + private final GridBagConstraints availableBackendsPanelContstraints = new GridBagConstraints(); + + private final JButton okayButton; + private final JButton cancelButton; + + private final JList<String> agentList; + private final DefaultListModel<String> listModel; + + + public AgentConfigurationFrame() { + assertInEDT(); + + setTitle(localize(LocaleResources.CONFIGURE_AGENT_WINDOW_TITLE)); + + JLabel lblEnabledisableBackends = new JLabel(localize(LocaleResources.CONFIGURE_ENABLE_BACKENDS)); + + availableBackendsPanel = new JPanel(); + + okayButton = new JButton(localize(LocaleResources.BUTTON_OK)); + okayButton.addActionListener(this); + + cancelButton = new JButton(localize(LocaleResources.BUTTON_CANCEL)); + cancelButton.addActionListener(this); + + JScrollPane scrollPane = new JScrollPane(); + + JLabel lblAgents = new JLabel(localize(LocaleResources.CONFIGURE_AGENT_AGENTS_LIST)); + + GroupLayout groupLayout = new GroupLayout(getContentPane()); + groupLayout.setHorizontalGroup( + groupLayout.createParallelGroup(Alignment.LEADING) + .addGroup(groupLayout.createSequentialGroup() + .addContainerGap() + .addGroup(groupLayout.createParallelGroup(Alignment.LEADING) + .addGroup(groupLayout.createSequentialGroup() + .addComponent(scrollPane, GroupLayout.PREFERRED_SIZE, 127, GroupLayout.PREFERRED_SIZE) + .addGap(0) + .addGroup(groupLayout.createParallelGroup(Alignment.LEADING) + .addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup() + .addComponent(cancelButton) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent(okayButton)) + .addGroup(groupLayout.createSequentialGroup() + .addGap(12) + .addGroup(groupLayout.createParallelGroup(Alignment.LEADING) + .addGroup(groupLayout.createSequentialGroup() + .addGap(12) + .addComponent(availableBackendsPanel, GroupLayout.DEFAULT_SIZE, 540, Short.MAX_VALUE)) + .addComponent(lblEnabledisableBackends))))) + .addComponent(lblAgents)) + .addContainerGap()) + ); + groupLayout.setVerticalGroup( + groupLayout.createParallelGroup(Alignment.TRAILING) + .addGroup(groupLayout.createSequentialGroup() + .addGap(6) + .addComponent(lblAgents) + .addPreferredGap(ComponentPlacement.UNRELATED) + .addGroup(groupLayout.createParallelGroup(Alignment.TRAILING) + .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 413, Short.MAX_VALUE) + .addGroup(groupLayout.createSequentialGroup() + .addComponent(lblEnabledisableBackends) + .addGap(2) + .addComponent(availableBackendsPanel, GroupLayout.DEFAULT_SIZE, 365, Short.MAX_VALUE) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(groupLayout.createParallelGroup(Alignment.BASELINE) + .addComponent(okayButton) + .addComponent(cancelButton)))) + .addContainerGap()) + ); + + listModel = new DefaultListModel<String>(); + agentList = new JList<String>(listModel); + agentList.addListSelectionListener(this); + scrollPane.setViewportView(agentList); + + availableBackendsPanel.setLayout(new GridBagLayout()); + getContentPane().setLayout(groupLayout); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + fireAction(new ActionEvent<>(AgentConfigurationFrame.this, ConfigurationAction.CLOSE_CANCEL)); + } + }); + } + + private void resetConstraints() { + availableBackendsPanelContstraints.gridwidth = 1; + availableBackendsPanelContstraints.gridy = 0; + availableBackendsPanelContstraints.gridx = 0; + availableBackendsPanelContstraints.weightx = 0; + availableBackendsPanelContstraints.weighty = 0; + availableBackendsPanelContstraints.anchor = GridBagConstraints.LINE_START; + availableBackendsPanelContstraints.fill = GridBagConstraints.BOTH; + } + + + @Override + public void addActionListener(ActionListener<ConfigurationAction> listener) { + listeners.add(listener); + } + + @Override + public void removeActionListener(ActionListener<ConfigurationAction> listener) { + listeners.remove(listener); + } + + @Override + public void addAgent(final String agentName) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + listModel.addElement(agentName); + } + }); + } + + @Override + public String getSelectedAgent() { + assertInEDT(); + return agentList.getSelectedValue(); + } + + @Override + public void clearAllAgents() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + listModel.clear(); + } + }); + } + + + @Override + public void setBackendStatus(final Map<String, Boolean> backendStatus) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + backends.clear(); + availableBackendsPanel.removeAll(); + resetConstraints(); + + for (Entry<String, Boolean> entry: backendStatus.entrySet()) { + String backendName = entry.getKey(); + boolean checked = entry.getValue(); + JCheckBox checkBox = new JCheckBox(backendName); + checkBox.setSelected(checked); + checkBox.setActionCommand(backendName); + checkBox.addActionListener(AgentConfigurationFrame.this); + backends.put(backendName, checkBox); + availableBackendsPanel.add(checkBox, availableBackendsPanelContstraints); + availableBackendsPanelContstraints.gridy++; + } + availableBackendsPanelContstraints.weighty = 1.0; + availableBackendsPanelContstraints.weightx = 1.0; + availableBackendsPanelContstraints.fill = GridBagConstraints.BOTH; + availableBackendsPanel.add(Box.createGlue(), availableBackendsPanelContstraints); + AgentConfigurationFrame.this.revalidate(); + } + }); + } + + @Override + public Map<String, Boolean> getBackendStatus() { + assertInEDT(); + + Map<String,Boolean> latestUserSpecified = new HashMap<>(); + for (Entry<String, JCheckBox> entry: backends.entrySet()) { + latestUserSpecified.put(entry.getKey(), entry.getValue().isSelected()); + } + return latestUserSpecified; + } + + @Override + public void showDialog() { + assertInEDT(); + + pack(); + setVisible(true); + + agentList.setSelectedIndex(0); + } + + @Override + public void hideDialog() { + assertInEDT(); + + setVisible(false); + dispose(); + } + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + Object source = e.getSource(); + if (source == okayButton) { + fireAction(new ActionEvent<>(this, ConfigurationAction.CLOSE_ACCEPT)); + } else if (source == cancelButton) { + fireAction(new ActionEvent<>(this, ConfigurationAction.CLOSE_CANCEL)); + } + } + + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getSource() == agentList) { + if (e.getValueIsAdjusting()) { + return; + } + fireAction(new ActionEvent<>(this, ConfigurationAction.SWITCH_AGENT)); + } else { + throw new IllegalStateException("unknown trigger"); + } + } + + private void fireAction(ActionEvent<ConfigurationAction> actionEvent) { + for (ActionListener<ConfigurationAction> l: listeners) { + l.actionPerformed(actionEvent); + } + } + + private static void assertInEDT() { + if (!SwingUtilities.isEventDispatchThread()) { + throw new IllegalStateException("must be called from within the swing EDT"); + } + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationModel.java Thu Apr 05 15:45:55 2012 -0400 @@ -0,0 +1,92 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.redhat.thermostat.client.AgentConfigurationSource; + +/** + * This model sits between the current view and the remote model, and allows + * us to make changes and later throw them away. + */ +public class AgentConfigurationModel { + + private final AgentConfigurationSource remoteConfiguration; + + private final List<String> knownAgents; + private Map<String, Map<String, Boolean>> enabledBackends; + + public AgentConfigurationModel(AgentConfigurationSource configSource) { + this.remoteConfiguration = configSource; + + knownAgents = new ArrayList<>(remoteConfiguration.getKnownAgents()); + enabledBackends = new HashMap<>(); + for (String agent: knownAgents) { + enabledBackends.put(agent, new HashMap<String, Boolean>(remoteConfiguration.getAgentBackends(agent))); + } + } + + public Collection<String> getAgents() { + return Collections.unmodifiableList(knownAgents); + } + + public Collection<String> getBackends(String agentName) { + return Collections.unmodifiableSet(enabledBackends.get(agentName).keySet()); + } + + public void setBackendEnabled(String agentName, String backendName, boolean enabled) { + enabledBackends.get(agentName).put(backendName, enabled); + } + + public boolean getAgentBackendEnabled(String agentName, String backendName) { + return enabledBackends.get(agentName).get(backendName); + } + + public void saveConfiguration() { + for (Entry<String, Map<String, Boolean>> entry: enabledBackends.entrySet()) { + remoteConfiguration.updateAgentConfig(entry.getKey(), entry.getValue()); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationView.java Thu Apr 05 15:45:55 2012 -0400 @@ -0,0 +1,69 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import java.util.Map; + +import com.redhat.thermostat.common.ActionListener; + +public interface AgentConfigurationView { + + enum ConfigurationAction { + SWITCH_AGENT, + CLOSE_ACCEPT, + CLOSE_CANCEL, + } + + void showDialog(); + + void hideDialog(); + + void addActionListener(ActionListener<ConfigurationAction> listener); + + void removeActionListener(ActionListener<ConfigurationAction> listener); + + void addAgent(String agentName); + + String getSelectedAgent(); + + void clearAllAgents(); + + void setBackendStatus(Map<String,Boolean> agentConfiguration); + + Map<String,Boolean> getBackendStatus(); + +}
--- a/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java Thu Apr 12 10:38:24 2012 -0400 +++ b/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java Thu Apr 05 15:45:55 2012 -0400 @@ -43,6 +43,7 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.Insets; +import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; @@ -84,6 +85,8 @@ import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import com.redhat.thermostat.client.Configuration; +import com.redhat.thermostat.client.AgentConfigurationSource; import com.redhat.thermostat.client.ApplicationInfo; import com.redhat.thermostat.client.HostsVMsLoader; import com.redhat.thermostat.client.MainView; @@ -223,17 +226,17 @@ private final ShutdownClient shutdownAction; - private ApplicationInfo appInfo; + private ApplicationInfo appInfo; private ActionNotifier<Action> actionNotifier = new ActionNotifier<>(this); - private final DefaultMutableTreeNode publishedRoot = + private final DefaultMutableTreeNode publishedRoot = new DefaultMutableTreeNode(localize(LocaleResources.MAIN_WINDOW_TREE_ROOT_NAME)); private final DefaultTreeModel publishedTreeModel = new DefaultTreeModel(publishedRoot); public MainWindow(UiFacadeFactory facadeFactory) { super(); - + appInfo = new ApplicationInfo(); setTitle(appInfo.getName()); @@ -292,6 +295,18 @@ fileExitMenu.addActionListener(shutdownAction); fileMenu.add(fileExitMenu); + JMenu editMenu = new JMenu(localize(LocaleResources.MENU_EDIT)); + mainMenuBar.add(editMenu); + + JMenuItem configureAgentMenuItem = new JMenuItem(localize(LocaleResources.MENU_EDIT_CONFIGURE_AGENT)); + configureAgentMenuItem.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + new Configuration().showAgentConfiguration(); + } + }); + editMenu.add(configureAgentMenuItem); + JMenu helpMenu = new JMenu(localize(LocaleResources.MENU_HELP)); helpMenu.getPopupMenu().setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); mainMenuBar.add(helpMenu); @@ -499,6 +514,7 @@ } } + @Override public void addActionListener(ActionListener<Action> l) { actionNotifier.addActionListener(l); } @@ -511,6 +527,7 @@ actionNotifier.fireAction(action); } + @Override public void updateTree(String filter, HostsVMsLoader hostsVMsLoader) { BackgroundTreeModelWorker worker = new BackgroundTreeModelWorker(publishedTreeModel, publishedRoot, filter, hostsVMsLoader); worker.execute();
--- a/client/src/main/resources/com/redhat/thermostat/client/locale/strings.properties Thu Apr 12 10:38:24 2012 -0400 +++ b/client/src/main/resources/com/redhat/thermostat/client/locale/strings.properties Thu Apr 05 15:45:55 2012 -0400 @@ -15,6 +15,8 @@ MENU_FILE_IMPORT = Import MENU_FILE_EXPORT = Export MENU_FILE_EXIT = Exit +MENU_EDIT = Edit +MENU_EDIT_CONFIGURE_AGENT = Configure Agent... MENU_HELP = Help MENU_HELP_ABOUT = About @@ -135,3 +137,7 @@ VM_CLASSES_CHART_REAL_TIME_LABEL = Time VM_CLASSES_CHART_LOADED_CLASSES_LABEL = Number of loaded classes VM_INFO_TAB_CLASSES = Classes + +CONFIGURE_AGENT_WINDOW_TITLE = Configure Agent Backends +CONFIGURE_AGENT_AGENTS_LIST = Agents +CONFIGURE_ENABLE_BACKENDS = Enable/Disable Backends \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/test/java/com/redhat/thermostat/client/ui/AgentConfigurationControllerTest.java Thu Apr 05 15:45:55 2012 -0400 @@ -0,0 +1,233 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import com.redhat.thermostat.client.AgentConfigurationSource; +import com.redhat.thermostat.client.ui.AgentConfigurationView.ConfigurationAction; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.TimerFactory; +import com.redhat.thermostat.common.appctx.ApplicationContext; +import com.redhat.thermostat.common.appctx.ApplicationContextUtil; + +public class AgentConfigurationControllerTest { + + @Before + public void setUp() { + ApplicationContextUtil.resetApplicationContext(); + + /* + * Set up a mock timer factory that always executes actions synchronously on start(); + */ + TimerFactory tf = mock(TimerFactory.class); + Timer timer = mock(Timer.class); + final Runnable[] runnable = new Runnable[1]; + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + runnable[0] = (Runnable) invocation.getArguments()[0]; + return null; + } + + }).when(timer).setAction(any(Runnable.class)); + + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + runnable[0].run(); + return null; + } + + }).when(timer).start(); + when(tf.createTimer()).thenReturn(timer); + ApplicationContext.getInstance().setTimerFactory(tf); + + } + + @After + public void tearDown() { + ApplicationContextUtil.resetApplicationContext(); + } + + @Test + public void testAddingEnabledBackends() { + AgentConfigurationView view = mock(AgentConfigurationView.class); + AgentConfigurationModel model = mock(AgentConfigurationModel.class); + when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"})); + List<String> backends = Arrays.asList(new String[] {"backend1", "backend2"}); + when(model.getBackends(any(String.class))).thenReturn(backends); + when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true); + + Map<String,Boolean> expected = new HashMap<>(); + expected.put("backend1", true); + expected.put("backend2", true); + + AgentConfigurationController controller = new AgentConfigurationController(model, view); + controller.showView(); + controller.hideView(); + + verify(view).addAgent("agent1"); + verify(view).setBackendStatus(eq(expected)); + verify(view).showDialog(); + verify(view).hideDialog(); + } + + @Test + public void testAddingDisabledBackends() { + AgentConfigurationView view = mock(AgentConfigurationView.class); + AgentConfigurationModel model = mock(AgentConfigurationModel.class); + when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"})); + List<String> backends = Arrays.asList(new String[] {"backend1",}); + when(model.getBackends(any(String.class))).thenReturn(backends); + when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(false); + + Map<String,Boolean> expected = new HashMap<>(); + expected.put("backend1", false); + + AgentConfigurationController controller = new AgentConfigurationController(model, view); + controller.showView(); + controller.hideView(); + + verify(view).addAgent("agent1"); + verify(view).setBackendStatus(eq(expected)); + verify(view).showDialog(); + verify(view).hideDialog(); + } + + /** + * Verify that the accepting the changes signals the controller + */ + @Test + public void testViewEditedBackends() { + final ActionListener<ConfigurationAction>[] listeners = (ActionListener<ConfigurationAction>[]) new ActionListener<?>[1]; + AgentConfigurationView view = mock(AgentConfigurationView.class); + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + listeners[0] = (ActionListener<ConfigurationAction>) invocation.getArguments()[0]; + return null; + } + }).when(view).addActionListener(any(ActionListener.class)); + + + AgentConfigurationModel model = mock(AgentConfigurationModel.class); + when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"})); + List<String> backends = Arrays.asList(new String[] {"backend1"}); + when(model.getBackends(any(String.class))).thenReturn(backends); + when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true); + + Map<String,Boolean> expected = new HashMap<>(); + expected.put("backend1", true); + + AgentConfigurationController controller = new AgentConfigurationController(model, view); + controller.showView(); + + listeners[0].actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.CLOSE_ACCEPT)); + + controller.hideView(); + + InOrder inOrder = inOrder(view); + + inOrder.verify(view).addAgent("agent1"); + inOrder.verify(view).setBackendStatus(eq(expected)); + inOrder.verify(view).getBackendStatus(); + + verify(model).saveConfiguration(); + } + + /** + * Verify that controller handles cancel properly + */ + @Test + public void testViewCancelEditingBackends() { + final ActionListener<ConfigurationAction>[] listeners = (ActionListener<ConfigurationAction>[]) new ActionListener<?>[1]; + AgentConfigurationView view = mock(AgentConfigurationView.class); + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + listeners[0] = (ActionListener<ConfigurationAction>) invocation.getArguments()[0]; + return null; + } + }).when(view).addActionListener(any(ActionListener.class)); + + + AgentConfigurationModel model = mock(AgentConfigurationModel.class); + when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"})); + List<String> backends = Arrays.asList(new String[] {"backend1"}); + when(model.getBackends(any(String.class))).thenReturn(backends); + when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true); + + Map<String,Boolean> expectedConfig = new HashMap<>(); + expectedConfig.put("backend1", true); + + AgentConfigurationController controller = new AgentConfigurationController(model, view); + controller.showView(); + + listeners[0].actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.CLOSE_CANCEL)); + + controller.hideView(); + + verify(view).addAgent("agent1"); + verify(view).setBackendStatus(eq(expectedConfig)); + verify(view, never()).getBackendStatus(); + + verify(model, never()).saveConfiguration(); + } +}