/*
* Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved.
*/
package jsystem.treeui.agents;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.remote.JMXConnectionNotification;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import jsystem.framework.scenario.DistributedExecutionHelper;
import jsystem.framework.scenario.ScenariosManager;
import jsystem.runner.ErrorLevel;
import jsystem.runner.agent.clients.JSystemAgentClient;
import jsystem.runner.agent.server.RunnerEngine;
import jsystem.runner.agent.server.RunnerEngine.ConnectionState;
import jsystem.treeui.RemoteAgentUIComponents;
import jsystem.treeui.actionItems.IgnisAction;
import jsystem.treeui.actionItems.SaveScenarioAction;
import jsystem.treeui.client.JSystemAgentClientsPool;
import jsystem.treeui.client.RemoteAgentClient;
import jsystem.treeui.error.ErrorPanel;
import jsystem.treeui.images.ImageCenter;
import jsystem.treeui.reporter.ReportersPanel;
import jsystem.treeui.utilities.ProgressBarPanel;
import jsystem.treeui.utilities.Task;
import jsystem.utils.ProgressNotifier;
import jsystem.utils.StringUtils;
/**
* Agents management dialog.
*
* @author goland
*/
public class AgentsDialog extends JDialog {
private final String NEW_AGENT_TEXT = "enter url here";
private static final ImageIcon OKAY_IMAGE = ImageCenter.getInstance().getImage(ImageCenter.ICON_REMOTEAGENT_OK);
private static final ImageIcon ERROR_IMAGE = ImageCenter.getInstance().getImage(ImageCenter.ICON_REMOTEAGENT_PROBLEM);
private static final ImageIcon NA_IMAGE = ImageCenter.getInstance().getImage(ImageCenter.ICON_REMOTEAGENT_NOTCONNECTED);
private static final long serialVersionUID = 1L;
private AgentListTableModel agentsListTableModel;
private List<AgentListDialogAction> actions = new ArrayList<AgentListDialogAction>();
private int editableRow = -1;
private JTable table;
private Set<String> selectedHosts;
private AgentsConnectionStatusListener connectionStatusListener;
public AgentsDialog(Dialog owner, boolean modal) {
super(owner, modal);
}
public AgentsDialog() {
super();
}
public static void showAgentsDialog() throws Exception {
AgentsDialog dialog = new AgentsDialog();
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
dialog.initDialog();
dialog.setVisible(true);
}
public void initDialog() throws Exception {
setTitle("Agents List");
connectionStatusListener = new AgentsConnectionStatusListener();
SwingUtilities.windowForComponent(this).setIconImage(ImageCenter.getInstance().getAwtImage(ImageCenter.ICON_JSYSTEM));
JPanel mainPanel = new JPanel(new BorderLayout());
selectedHosts = new HashSet<String>();
String[] participatingHosts = DistributedExecutionHelper.getParticipatingHosts();
for (String s : participatingHosts) {
selectedHosts.add(s);
}
buildAgentsListTableModel();
table = new JTable(agentsListTableModel);
table.setRowSelectionAllowed(true);
table.getColumnModel().getColumn(0).setCellRenderer(new AgentStatusCellRenderer());
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
JScrollPane tablescroll = new JScrollPane(table);
table.setFillsViewportHeight(true);
mainPanel.add(tablescroll, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel();
AgentListDialogAction a = new AddAgentAction(table);
buttonsPanel.add(new JButton(a));
a = new RemoveAgentAction(table);
actions.add(a);
buttonsPanel.add(new JButton(a));
a = new SynchronizeAction(table);
actions.add(a);
buttonsPanel.add(new JButton(a));
a = new RefreshAgentAction(table);
actions.add(a);
buttonsPanel.add(new JButton(a));
buttonsPanel.add(new JButton(new CloseAction(this)));
mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
setContentPane(mainPanel);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;
setLocation(screenWidth / 4, screenHeight / 5);
enableActions(false);
table.getSelectionModel().addListSelectionListener(new AgentsTableListSelectionListener());
table.getColumnModel().getColumn(1).setPreferredWidth(200);
table.getTableHeader().setReorderingAllowed(false);
pack();
addWindowListener(new AgentDialogListener());
}
private void buildAgentsListTableModel() throws Exception {
Vector<Object> model = new Vector<Object>();
RunnerEngine[] clients = JSystemAgentClientsPool.getClients(null);
for (RunnerEngine client : clients) {
Vector<Object> clientRow = getJSystemAgentDataVector(client, false);
model.add(clientRow);
}
Vector<String> columns = new Vector<String>();
columns.add("Status");
columns.add("URL");
columns.add("Project");
columns.add("Scenario");
columns.add("State");
columns.add("Version");
agentsListTableModel = new AgentListTableModel(model, columns);
agentsListTableModel.addTableModelListener(new AgentsTableModelListener());
}
private Vector<Object> getJSystemAgentDataVector(RunnerEngine client, boolean refresh) throws Exception {
Vector<Object> data = new Vector<Object>();
if (!client.getConnectionState().equals(ConnectionState.connected) && refresh) {
try {
client.init();
} catch (Throwable e) {
}
} else {
client.removeListener(connectionStatusListener);
client.addListener(connectionStatusListener);
}
if (!client.getConnectionState().equals(ConnectionState.connected)) {
data.add(ERROR_IMAGE);
data.add(client.getId());
} else {
data.add(OKAY_IMAGE);
data.add(client.getId());
data.add(client.getCurrentProjectName());
data.add(client.getActiveScenario());
data.add(client.getEngineExecutionState().toString());
data.add(client.getEngineVersion());
}
return data;
}
private void enableActions(boolean enabled) {
for (AgentListDialogAction a : actions) {
a.setEnabled(enabled);
}
}
private RunnerEngine[] selectedRowsToClients() {
int[] selectedRows = table.getSelectedRows();
RunnerEngine[] clients = new RunnerEngine[selectedRows.length];
for (int i = 0; i < selectedRows.length; i++) {
String url = table.getValueAt(selectedRows[i], 1).toString();
clients[i] = JSystemAgentClientsPool.getClient(url);
}
return clients;
}
private ProgressNotifier openProgressDialog(String title) {
Point p = getLocation();
p.setLocation(p.x + 10, p.y + 50);
ProgressNotifier notifier = ProgressBarPanel.createAndShowProgressPanel(title, p, false, this);
return notifier;
}
private void refreshUrl(String url, ProgressNotifier notifier, int progress) {
RunnerEngine client = JSystemAgentClientsPool.getClient(url);
int index = agentsListTableModel.getAgentRow(url);
try {
Vector<Object> v = getJSystemAgentDataVector(client, true);
agentsListTableModel.removeRow(index);
agentsListTableModel.insertRow(index, v);
table.addRowSelectionInterval(index, index);
if (notifier != null) {
notifier.notifyProgress("Finished " + url + " refresh", progress);
}
} catch (Exception e1) {
agentsListTableModel.setValueAt(ERROR_IMAGE, index, 0);
if (notifier != null) {
notifier.notifyProgress("Failed refreshing " + url + " " + e1.getMessage(), progress);
}
}
}
class AgentListTableModel extends DefaultTableModel {
AgentListTableModel(Vector<Object> model, Vector<String> columns) {
super(model, columns);
}
private static final long serialVersionUID = 1L;
@Override
public boolean isCellEditable(int row, int column) {
return (row == editableRow) && (column == 1);
};
public int getAgentRow(String agentUrl) {
for (int i = 0; i < getRowCount(); i++) {
String url = (String) getValueAt(i, 1);
if (url.equals(agentUrl)) {
return i;
}
}
return -1;
}
public void removeRow(String urlToRemove) {
for (int i = 0; i < getRowCount(); i++) {
String url = (String) getValueAt(i, 1);
if (urlToRemove.equals(url)) {
removeRow(i);
}
}
}
public void refreshAgent(String url) {
int row = getAgentRow(url);
fireTableRowsUpdated(row, row);
}
}
class AgentStatusCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String url = (String) agentsListTableModel.getValueAt(row, 1);
RunnerEngine engine = JSystemAgentClientsPool.getClient(url);
if (engine == null) {
label.setIcon(NA_IMAGE);
} else {
if (engine.getConnectionState().equals(ConnectionState.connected)) {
label.setIcon(OKAY_IMAGE);
} else if (engine.getConnectionState().equals(ConnectionState.disconnected)) {
label.setIcon(ERROR_IMAGE);
} else {
label.setIcon(NA_IMAGE);
}
}
if (selectedHosts.contains(url)) {
label.setText("**");
} else {
label.setText("");
}
return label;
}
}
/**
* Actions and events
*/
/**
* Rows selected. Operations buttons availability is updated according to number of selected rows.
*/
class AgentsTableListSelectionListener implements ListSelectionListener {
@Override
public void valueChanged(ListSelectionEvent e) {
int count = table.getSelectedRowCount();
enableActions(count > 0);
}
}
/**
* Table changed. table is allowed to change only after user has pressed on the add agent button. The event is invoked in the following
* cases: once after row is added, second time after user edits the url, in addition the event is called when row is removed. In case of
* remove, and add, ignoring event.
*/
class AgentsTableModelListener implements TableModelListener {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() != TableModelEvent.UPDATE) {
return;
}
int column = table.getSelectedColumn();
int row = table.getSelectedRow();
if (column != -1 && row != -1) {
final String data = table.getValueAt(row, column).toString();
if (!NEW_AGENT_TEXT.equals(data)) {
try {
RunnerEngine engine = JSystemAgentClientsPool.getClient(data);
if (engine != null) {
ErrorPanel.showErrorDialog("Agent " + data + " already exists in the agents list", "", ErrorLevel.Info);
agentsListTableModel.setValueAt(NEW_AGENT_TEXT, row, column);
return;
}
JSystemAgentClientsPool.addClient(data, true);
editableRow = -1;
} catch (Exception e1) {
ErrorPanel.showErrorDialog("Failed adding " + data, e1, ErrorLevel.Error);
} finally {
ReportersPanel.addAgent(JSystemAgentClientsPool.getClient(data));
RemoteAgentUIComponents.refreshAgentList();
}
}
}
}
}
/**
* Synchronize button is pressed. Synchronizing agent project with local project.
*/
class SynchronizeAction extends AgentListDialogAction {
private static final long serialVersionUID = 1L;
SynchronizeAction(JTable table) {
super(table);
putValue(Action.SHORT_DESCRIPTION, "Synchronize agent project with local project");
putValue(Action.NAME, "Synchronize");
putValue(Action.ACTION_COMMAND_KEY, "synchronize");
}
@Override
public void actionPerformed(ActionEvent e) {
if (ScenariosManager.isDirty()) {
try {
SaveScenarioAction.getInstance().saveCurrentScenario();
} catch (Exception e1) {
ErrorPanel.showErrorDialog("Failed Saving Scenario", e1, ErrorLevel.Error);
}
}
final RunnerEngine[] clients = selectedRowsToClients();
ProgressNotifier notifier = openProgressDialog("Synchronizing agents");
try {
Task syncTask = new Task(notifier) {
@Override
protected Void doInBackground() throws Exception {
JSystemAgentClient[] jsysClients = new JSystemAgentClient[clients.length];
int j = 0;
for (RunnerEngine client : clients) {
jsysClients[j++] = (JSystemAgentClient) client;
}
RemoteAgentClient.syncAgentsWithLocalProject(jsysClients, true, this, false);
int progress = 90;
for (RunnerEngine client : clients) {
if (client.getConnectionState() == ConnectionState.connected) {
refreshUrl(client.getId(), this, progress += 3);
} else {
notifyProgress("no connection to " + client.getId() + " skipping refresh", progress += 3);
}
}
return null;
}
};
syncTask.execute();
} catch (Throwable e1) {
ErrorPanel.showErrorDialog("Failed synchronizing agents", StringUtils.getStackTrace(e1), ErrorLevel.Error);
}
}
}
/**
* Refresh button is pressed. Fetching agent information;
*/
class RefreshAgentAction extends AgentListDialogAction {
private static final long serialVersionUID = 1L;
RefreshAgentAction(JTable table) {
super(table);
putValue(Action.NAME, "Refresh");
putValue(Action.SHORT_DESCRIPTION, "Refresh agent information");
putValue(Action.ACTION_COMMAND_KEY, "refresh");
}
@Override
public void actionPerformed(ActionEvent e) {
final int[] selectedRows = table.getSelectedRows();
ProgressNotifier notifier = openProgressDialog("Refreshing Agents");
Task refreshTask = new Task(notifier) {
@Override
protected Void doInBackground() throws Exception {
int i = 0;
for (int row : selectedRows) {
String url = table.getValueAt(row, 1).toString();
refreshUrl(url, this, ++i * (100 / selectedRows.length));
}
return null;
}
};
refreshTask.execute();
}
}
/**
* Add agent is pressed. Adding a line to the agents table and allowing the user to edit the line.
*/
class AddAgentAction extends AgentListDialogAction {
private static final long serialVersionUID = 1L;
AddAgentAction(JTable table) {
super(table);
putValue(Action.SHORT_DESCRIPTION, "Add remote agent to agents list");
putValue(Action.NAME, "Add");
putValue(Action.ACTION_COMMAND_KEY, "add");
}
@Override
public void actionPerformed(ActionEvent e) {
int count = agentsListTableModel.getRowCount();
if (count > 0 && agentsListTableModel.getValueAt(count - 1, 1).equals(NEW_AGENT_TEXT)) {
return;
}
Vector<String> v = new Vector<String>();
v.add("");
v.add(NEW_AGENT_TEXT);
agentsListTableModel.addRow(v);
editableRow = agentsListTableModel.getRowCount() - 1;
}
}
/**
* Removing agent from agents list
*/
class RemoveAgentAction extends AgentListDialogAction {
private static final long serialVersionUID = 1L;
RemoveAgentAction(JTable table) {
super(table);
putValue(Action.SHORT_DESCRIPTION, "Synchronize agent project with local project");
putValue(Action.NAME, "Remove");
putValue(Action.ACTION_COMMAND_KEY, "remove");
}
@Override
public void actionPerformed(ActionEvent e) {
int[] selectedRows = agentTable.getSelectedRows();
String[] urls = new String[selectedRows.length];
for (int i = 0; i < selectedRows.length; i++) {
String url = (String) agentTable.getValueAt(selectedRows[i], 1);
urls[i] = url;
}
for (String url : urls) {
JSystemAgentClientsPool.removeClient(url);
agentsListTableModel.removeRow(url);
ReportersPanel.removeAgent(url);
RemoteAgentUIComponents.refreshAgentList();
}
}
}
/**
* Dialog is closed
*/
class CloseAction extends IgnisAction {
private static final long serialVersionUID = 1L;
private JDialog dialog;
CloseAction(JDialog dialog) {
putValue(Action.SHORT_DESCRIPTION, "Synchronize agent project with local project");
putValue(Action.NAME, "Close");
putValue(Action.ACTION_COMMAND_KEY, "close");
this.dialog = dialog;
}
@Override
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
dialog.dispose();
}
}
/**
*/
abstract class AgentListDialogAction extends IgnisAction {
private static final long serialVersionUID = 1L;
protected JTable agentTable;
protected AgentListDialogAction(JTable agentsTable) {
this.agentTable = agentsTable;
}
}
public class AgentsConnectionStatusListener implements NotificationListener {
@Override
public void handleNotification(Notification notification, Object handback) {
if (!(notification instanceof JMXConnectionNotification)) {
return;
}
table.repaint();
}
}
public class AgentDialogListener implements WindowListener {
@Override
public void windowActivated(WindowEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void windowClosed(WindowEvent arg0) {
RunnerEngine[] engines = JSystemAgentClientsPool.getClients(null);
for (RunnerEngine e : engines) {
e.removeListener(connectionStatusListener);
}
}
@Override
public void windowClosing(WindowEvent arg0) {
}
@Override
public void windowDeactivated(WindowEvent arg0) {
windowClosed(arg0);
}
@Override
public void windowDeiconified(WindowEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void windowIconified(WindowEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void windowOpened(WindowEvent arg0) {
// TODO Auto-generated method stub
}
}
}