/*
* Copyright (C) 2008 Yohan Liyanage.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nebulaframework.ui.swing.node;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nebulaframework.discovery.DiscoveryFailureException;
import org.nebulaframework.grid.Grid;
import org.nebulaframework.grid.cluster.node.GridNode;
import org.nebulaframework.grid.service.event.ServiceEventsSupport;
import org.nebulaframework.grid.service.event.ServiceHookCallback;
import org.nebulaframework.grid.service.message.ServiceMessage;
import org.nebulaframework.grid.service.message.ServiceMessageType;
import org.nebulaframework.ui.swing.AboutDialog;
import org.nebulaframework.ui.swing.UISupport;
import org.nebulaframework.util.log4j.JLabelAppender;
import org.nebulaframework.util.log4j.JTextPaneAppender;
import org.nebulaframework.util.net.NetUtils;
import org.nebulaframework.util.profiling.TimeUtils;
/**
* The Swing UI for GridNode. This UI exposes basic functionality of
* GridNode, and allows users to easily manage the Node.
* <p>
* However, for more advanced uses, such as embedding
* GridNodes, consider using the API.
*
* @author Yohan Liyanage
* @version 1.0
*/
public class NodeMainUI extends JFrame {
private static final long serialVersionUID = 1574154489795768861L;
private static final Log log = LogFactory.getLog(NodeMainUI.class);
private static final int WIDTH = 600;
private static final int HEIGHT = 575;
// Components
private static Map<String, JComponent> components = new HashMap<String, JComponent>();
// Job History List
private JobHistoryListModel historyList = new JobHistoryListModel();
// Total Execution Time
private long executionTime = 0L;
// Auto Discovery Enabled
private boolean autodiscover = true;
// Last Discovery Attempt Time stamp
private Long lastDiscoveryAttempt = System.currentTimeMillis();
// Currently Active JobId
private String activeJobId = null;
private TrayIcon trayIcon;
private Image idleIcon;
private Image activeIcon;
/**
* Constructs a GridNode UI.
*
* @throws HeadlessException if UI not supported.
*/
public NodeMainUI() throws HeadlessException {
super();
setupUI();
}
/**
* UI Setup operations.
*/
private void setupUI() {
setTitle("Nebula Grid - Execution Node");
setSize(WIDTH, HEIGHT);
setJMenuBar(setupMenu());
setLayout(new BorderLayout());
JTabbedPane tabs = new JTabbedPane();
add(tabs, BorderLayout.CENTER);
tabs.addTab("Control Center", setupGeneral());
tabs.addTab("History", setupHistory());
resetActiveJobInfo();
setStatus("Not Connected");
updateGridInfo();
updateExecutionTime();
setupTrayIcon(this);
}
/**
* Setup System Tray Icon.
*
* @param frame owner frame
*/
private void setupTrayIcon(final JFrame frame) {
idleIcon = Toolkit.getDefaultToolkit()
.getImage(ClassLoader.getSystemResource("META-INF/resources/node_inactive.png"));
activeIcon = Toolkit.getDefaultToolkit()
.getImage(ClassLoader.getSystemResource("META-INF/resources/node_active.png"));
frame.setIconImage(idleIcon);
// If system tray is supported by OS
if (SystemTray.isSupported()) {
trayIcon = new TrayIcon(idleIcon,"Nebula Grid Node", createTrayPopup());
trayIcon.setImageAutoSize(true);
trayIcon.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton()==MouseEvent.BUTTON1) {
if (!frame.isVisible()) {
frame.setVisible(true);
}
frame.setExtendedState(JFrame.NORMAL);
frame.requestFocus();
frame.toFront();
}
}
});
try {
SystemTray.getSystemTray().add(trayIcon);
} catch (AWTException ae) {
log.debug("[UI] Unable to Initialize Tray Icon");
return;
}
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowIconified(WindowEvent e) {
// Hide (can be shown using tray icon)
frame.setVisible(false);
}
});
}
}
/**
* System Tray Icon Pop Up Menu
*
* @return PopupMenu
*/
private PopupMenu createTrayPopup() {
PopupMenu trayPopup = new PopupMenu();
// About
MenuItem aboutItem = new MenuItem("About");
aboutItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
showAbout();
}
});
trayPopup.add(aboutItem);
trayPopup.addSeparator();
// Shutdown Node
MenuItem shutdownItem = new MenuItem("Shutdown");
shutdownItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doShutdownNode();
}
});
trayPopup.add(shutdownItem);
return trayPopup;
}
/**
* Displays busy icon in System tray
*/
private void showBusyIcon() {
if (trayIcon!=null) trayIcon.setImage(activeIcon);
}
/**
* Displays Idle icon in System tray
*/
private void showIdleIcon() {
if (trayIcon!=null) trayIcon.setImage(idleIcon);
}
/**
* Removes System Tray icon.
*/
private void removeIcon() {
if (SystemTray.isSupported()) {
SystemTray.getSystemTray().remove(trayIcon);
}
}
/**
* Setup Menu Bar
* @return JMenu Bar
*/
private JMenuBar setupMenu() {
JMenuBar menuBar = new JMenuBar();
/* -- GridNode Menu -- */
JMenu gridNodeMenu = new JMenu("GridNode");
gridNodeMenu.setMnemonic(KeyEvent.VK_N);
menuBar.add(gridNodeMenu);
// Discover
JMenuItem clusterDiscoverItem = new JMenuItem("Disover and Connect Clusters");
clusterDiscoverItem.setMnemonic(KeyEvent.VK_D);
clusterDiscoverItem.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent.VK_F2, 0));
gridNodeMenu.add(clusterDiscoverItem);
clusterDiscoverItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doDiscover(false);
((JCheckBoxMenuItem)getUIElement("menu.node.autodiscover")).setSelected(false);
}
});
addUIElement("menu.node.discover", clusterDiscoverItem); // Add to components map
// Auto-Discovery
final JCheckBoxMenuItem autodiscoveryItem = new JCheckBoxMenuItem("Auto Discover");
autodiscoveryItem.setMnemonic(KeyEvent.VK_A);
autodiscoveryItem.setSelected(true);
gridNodeMenu.add(autodiscoveryItem);
autodiscoveryItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
autodiscover = autodiscoveryItem.isSelected();
}
});
addUIElement("menu.node.autodiscover", autodiscoveryItem); // Add to components map
gridNodeMenu.addSeparator();
// Cluster-> Shutdown
JMenuItem nodeShutdownItem = new JMenuItem("Shutdown", 'u');
nodeShutdownItem.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent.VK_F6, 0));
nodeShutdownItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doShutdownNode();
}
});
gridNodeMenu.add(nodeShutdownItem);
addUIElement("menu.node.shutdown",nodeShutdownItem); // Add to components map
/* -- Options Menu -- */
JMenu optionsMenu = new JMenu("Options");
optionsMenu.setMnemonic(KeyEvent.VK_O);
menuBar.add(optionsMenu);
// Configuration
JMenuItem optionsConfigItem = new JMenuItem("Configuration...", 'C');
optionsConfigItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showConfiguration();
}
});
optionsMenu.add(optionsConfigItem);
optionsConfigItem.setEnabled(false); // TODO Create Configuration Options
/* -- Help Menu -- */
JMenu helpMenu = new JMenu("Help");
helpMenu.setMnemonic(KeyEvent.VK_H);
menuBar.add(helpMenu);
// Help Contents
JMenuItem helpContentsItem = new JMenuItem("Help Contents", 'H');
helpContentsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
helpContentsItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showHelp();
}
});
helpMenu.add(helpContentsItem);
helpMenu.addSeparator();
JMenuItem helpAboutItem = new JMenuItem("About", 'A');
helpAboutItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showAbout();
}
});
helpMenu.add(helpAboutItem);
return menuBar;
}
/**
* Setup General (Control Center) Tab
*
* @return JPanel for Control Center
*/
private JPanel setupGeneral() {
JPanel generalPanel = new JPanel();
generalPanel.setLayout(new BorderLayout());
/* -- Stats Panel -- */
JPanel statsPanel = new JPanel();
generalPanel.add(statsPanel, BorderLayout.NORTH);
statsPanel.setLayout(new GridLayout(0, 2, 10, 10));
JPanel eastPanel = new JPanel();
statsPanel.add(eastPanel, BorderLayout.EAST);
eastPanel.setLayout(new BorderLayout());
JPanel westPanel = new JPanel();
statsPanel.add(westPanel, BorderLayout.WEST);
westPanel.setLayout(new BorderLayout());
// Grid Information Panel
JPanel gridInfoPanel = new JPanel();
eastPanel.add(gridInfoPanel, BorderLayout.NORTH);
gridInfoPanel.setBorder(BorderFactory.createTitledBorder("Grid Information"));
gridInfoPanel.setLayout(new GridLayout(0, 2, 10, 10));
JLabel nodeIdLabel = new JLabel("Node ID :");
gridInfoPanel.add(nodeIdLabel);
JLabel nodeId = new JLabel("#nodeid#");
gridInfoPanel.add(nodeId);
addUIElement("general.stats.nodeid", nodeId); // Add to components map
JLabel nodeIpLabel = new JLabel("Node IP :");
gridInfoPanel.add(nodeIpLabel);
JLabel nodeIp = new JLabel("#nodeip#");
gridInfoPanel.add(nodeIp);
addUIElement("general.stats.nodeip", nodeIp); // Add to components map
JLabel clusterIdLabel = new JLabel("Cluster ID :");
gridInfoPanel.add(clusterIdLabel);
JLabel clusterId = new JLabel("#clusterid#");
gridInfoPanel.add(clusterId);
addUIElement("general.stats.clusterid", clusterId); // Add to components map
JLabel clusterServiceLabel = new JLabel("Cluster Service :");
gridInfoPanel.add(clusterServiceLabel);
JLabel clusterService = new JLabel("#clusterservice#");
gridInfoPanel.add(clusterService);
addUIElement("general.stats.clusterservice", clusterService); // Add to components map
// Node Status Panel
JPanel nodeStatusPanel = new JPanel();
eastPanel.add(nodeStatusPanel, BorderLayout.SOUTH);
nodeStatusPanel.setBorder(BorderFactory.createTitledBorder("GridNode Status"));
nodeStatusPanel.setLayout(new GridLayout(0, 2, 10, 10));
JLabel statusLabel = new JLabel("Status :");
nodeStatusPanel.add(statusLabel);
JLabel status = new JLabel("#status#");
nodeStatusPanel.add(status);
addUIElement("general.stats.status", status); // Add to components map
JLabel uptimeLabel = new JLabel("Node Up Time :");
nodeStatusPanel.add(uptimeLabel);
JLabel uptime = new JLabel("#uptime#");
nodeStatusPanel.add(uptime);
addUIElement("general.stats.uptime", uptime); // Add to components map
JLabel execTimeLabel = new JLabel("Execution Time :");
nodeStatusPanel.add(execTimeLabel);
JLabel execTime = new JLabel("#exectime#");
nodeStatusPanel.add(execTime);
addUIElement("general.stats.exectime", execTime); // Add to components map
// Execution Statistics Panel
JPanel execStatsPanel = new JPanel();
westPanel.add(execStatsPanel, BorderLayout.NORTH);
execStatsPanel.setLayout(new GridLayout(0, 2, 10, 10));
execStatsPanel.setBorder(BorderFactory.createTitledBorder("Execution Statistics"));
JLabel totalJobsLabel = new JLabel("Total Jobs :");
execStatsPanel.add(totalJobsLabel);
JLabel totalJobs = new JLabel("0");
execStatsPanel.add(totalJobs);
addUIElement("general.stats.totaljobs", totalJobs); // Add to components map
JLabel totalTasksLabel = new JLabel("Total Tasks :");
execStatsPanel.add(totalTasksLabel);
JLabel totalTasks = new JLabel("0");
execStatsPanel.add(totalTasks);
addUIElement("general.stats.totaltasks", totalTasks); // Add to components map
JLabel totalBansLabel = new JLabel("Banments :");
execStatsPanel.add(totalBansLabel);
JLabel totalBans = new JLabel("0");
execStatsPanel.add(totalBans);
addUIElement("general.stats.totalbans", totalBans); // Add to components map
// Execution Active Job Panel
JPanel activeJobPanel = new JPanel();
westPanel.add(activeJobPanel, BorderLayout.SOUTH);
activeJobPanel.setLayout(new GridLayout(0, 2, 10, 10));
activeJobPanel.setBorder(BorderFactory.createTitledBorder("Active Job"));
JLabel jobNameLabel = new JLabel("GridJob Name :");
activeJobPanel.add(jobNameLabel);
JLabel jobName = new JLabel("#jobname#");
activeJobPanel.add(jobName);
addUIElement("general.stats.jobname", jobName); // Add to components map
JLabel durationLabel = new JLabel("Duration :");
activeJobPanel.add(durationLabel);
JLabel duration = new JLabel("#duration#");
activeJobPanel.add(duration);
addUIElement("general.stats.duration", duration); // Add to components map
JLabel tasksLabel = new JLabel("Tasks Executed :");
activeJobPanel.add(tasksLabel);
JLabel tasks = new JLabel("#xyz#");
activeJobPanel.add(tasks);
addUIElement("general.stats.tasks", tasks); // Add to components map
JLabel failuresLabel = new JLabel("Failures :");
activeJobPanel.add(failuresLabel);
JLabel failures = new JLabel("#failures#");
activeJobPanel.add(failures);
addUIElement("general.stats.failures", failures); // Add to components map
/* -- Log Panel -- */
JPanel logPanel = new JPanel();
generalPanel.add(logPanel, BorderLayout.CENTER);
logPanel.setLayout(new BorderLayout());
logPanel.setBorder(BorderFactory.createTitledBorder("Log Output"));
JTextPane logTextPane = new JTextPane();
logTextPane.setEditable(false);
logTextPane.setBackground(Color.BLACK);
logTextPane.setForeground(Color.WHITE);
logPanel.add(new JScrollPane(logTextPane,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED),
BorderLayout.CENTER);
addUIElement("general.log", logTextPane); // Add to component map
JPanel logOptionsPanel = new JPanel();
logPanel.add(logOptionsPanel, BorderLayout.SOUTH);
logOptionsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
final JCheckBox logScrollCheckbox = new JCheckBox("Auto-Scroll Log");
logScrollCheckbox.setSelected(true);
logScrollCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JTextPaneAppender.setAutoScroll(logScrollCheckbox.isSelected());
}
});
logOptionsPanel.add(logScrollCheckbox);
// Enable Logging
JTextPaneAppender.setTextPane(logTextPane);
/* -- Buttons Panel -- */
JPanel buttonsPanel = new JPanel();
generalPanel.add(buttonsPanel, BorderLayout.SOUTH);
buttonsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
// Shutdown Button
JButton shutdownButton = new JButton("Shutdown");
buttonsPanel.add(shutdownButton);
shutdownButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doShutdownNode();
}
});
// Start Up time Thread
Thread t = new Thread(new Runnable() {
public void run() {
long start = System.currentTimeMillis();
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.warn("Interrupted Exception in Up Time Thread",e);
}
final String uptime = TimeUtils.timeDifference(start);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JLabel upTime = getUIElement("general.stats.uptime");
upTime.setText(uptime);
}
});
}
}
});
t.setDaemon(true);
t.start();
// Auto-Discovery Thread
Thread autoDiscovery = new Thread(new Runnable() {
public void run() {
while (true) {
try {
// Attempt every 30 seconds
Thread.sleep(30000);
} catch (InterruptedException e) {
log.warn("Interrupted Exception in Up Time Thread",e);
}
if (autodiscover && (!Grid.isNode())) {
// 30 Second Intervals
doDiscover(true);
}
}
}
});
autoDiscovery.setDaemon(true);
autoDiscovery.start();
return generalPanel;
}
/**
* Setup Job History Tab Pane
*
* @return JPanel for History tab
*/
private JPanel setupHistory() {
JPanel historyPanel = new JPanel();
historyPanel.setLayout(new BorderLayout(10,10));
/* -- Job List -- */
JPanel jobListPanel = new JPanel();
historyPanel.add(jobListPanel, BorderLayout.CENTER);
jobListPanel.setLayout(new BorderLayout(10,10));
jobListPanel.setBorder(BorderFactory.createTitledBorder("Grid Jobs"));
final JList jobList = new JList(historyList);
JScrollPane jobListScroll = new JScrollPane(jobList);
jobListPanel.add(jobListScroll,BorderLayout.CENTER);
jobListScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
jobListScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
addUIElement("history.joblist", jobList);
jobList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jobList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
// Ignore intermediate events
if (e.getValueIsAdjusting()) return;
displayJobInfo(jobList.getSelectedValue());
}
});
JPanel jobInfoPanel = new JPanel();
historyPanel.add(jobInfoPanel, BorderLayout.SOUTH);
jobInfoPanel.setLayout(new BorderLayout(10,10));
jobInfoPanel.setBorder(BorderFactory.createTitledBorder("Job Information"));
JPanel centerPanel = new JPanel();
jobInfoPanel.add(centerPanel, BorderLayout.CENTER);
centerPanel.setLayout(new GridLayout(0,4, 10,10));
JLabel jobIdLabel = new JLabel("GridJob ID :");
centerPanel.add(jobIdLabel);
JLabel jobId = new JLabel("N/A");
centerPanel.add(jobId);
addUIElement("history.jobid", jobId); // Add to components map
JLabel jobNameLabel = new JLabel("GridJob Name :");
centerPanel.add(jobNameLabel);
JLabel jobName = new JLabel("N/A");
centerPanel.add(jobName);
addUIElement("history.jobname", jobName); // Add to components map
JLabel startTimeLabel = new JLabel("Start Time :");
centerPanel.add(startTimeLabel);
JLabel startTime = new JLabel("N/A");
centerPanel.add(startTime);
addUIElement("history.starttime", startTime); // Add to components map
JLabel durationLabel = new JLabel("Duration :");
centerPanel.add(durationLabel);
JLabel duration = new JLabel("N/A");
centerPanel.add(duration);
addUIElement("history.duration", duration); // Add to components map
JLabel tasksLabel = new JLabel("Tasks Executed :");
centerPanel.add(tasksLabel);
JLabel tasks = new JLabel("N/A");
centerPanel.add(tasks);
addUIElement("history.tasks", tasks); // Add to components map
JLabel failuresLabel = new JLabel("Failures :");
centerPanel.add(failuresLabel);
JLabel failures = new JLabel("N/A");
centerPanel.add(failures);
addUIElement("history.failures", failures); // Add to components map
// Place Holders
centerPanel.add(new JLabel());
centerPanel.add(new JLabel());
return historyPanel;
}
/**
* Resets the active Job Info fields
*/
protected void resetActiveJobInfo() {
JLabel jobName = getUIElement("general.stats.jobname");
JLabel duration = getUIElement("general.stats.duration");
JLabel tasks = getUIElement("general.stats.tasks");
JLabel failure = getUIElement("general.stats.failures");
jobName.setText("N/A");
duration.setText("N/A");
tasks.setText("N/A");
failure.setText("N/A");
}
/**
* Updates the GridNode Status to given status
* @param status status text
*/
protected void setStatus(String status) {
((JLabel)getUIElement("general.stats.status")).setText(status);
}
/**
* Displays the Job info for given {@link JobHistoryElement} in the
* History Tab Pane's fields.
*
* @param obj JobHistoryElement
*/
protected void displayJobInfo(Object obj) {
if (!(obj instanceof JobHistoryElement)) return;
JobHistoryElement element = (JobHistoryElement) obj;
JLabel jobId = getUIElement("history.jobid");
JLabel jobName = getUIElement("history.jobname");
JLabel starttime = getUIElement("history.starttime");
JLabel duration = getUIElement("history.duration");
JLabel tasks = getUIElement("history.tasks");
JLabel failures = getUIElement("history.failures");
jobId.setText(element.getJobId());
jobName.setText(element.getJobName());
starttime.setText(element.getStartTime());
duration.setText(element.getDuration());
tasks.setText(String.valueOf(element.getTasks()));
failures.setText(String.valueOf(element.getFailures()));
}
/**
* Updates GridInfo in Control Center
*/
private void updateGridInfo() {
JLabel nodeId = getUIElement("general.stats.nodeid");
JLabel nodeIp = getUIElement("general.stats.nodeip");
JLabel clusterId = getUIElement("general.stats.clusterid");
JLabel clusterService = getUIElement("general.stats.clusterservice");
if (Grid.isNode()) {
// If connected
GridNode instance = GridNode.getInstance();
nodeId.setText(instance.getId().toString());
nodeIp.setText(NetUtils.getLocalHostAddress());
clusterId.setText(instance.getClusterId().toString());
clusterService.setText(instance.getClusterUrl());
getUIElement("menu.node.discover").setEnabled(false);
registerHooks(instance);
setStatus("Idle");
}
else {
// If not connected
nodeId.setText("Not Connected");
nodeIp.setText("Not Connected");
clusterId.setText("Not Connected");
clusterService.setText("Not Connected");
setStatus("Not Connected");
getUIElement("menu.node.discover").setEnabled(true);
}
}
/**
* Registers Service Event Hooks to update fields, once
* connected
*
* @param instance GridNode instance
*/
private void registerHooks(GridNode instance) {
// Disconnection Hook
ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
@Override
public void onServiceEvent(ServiceMessage message) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateGridInfo();
}
});
}
}, instance.getClusterId().toString(), ServiceMessageType.NODE_DISCONNECTED);
// Job Start Hook
ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
@Override
public void onServiceEvent(ServiceMessage message) {
final String jobId = message.getMessage();
final String jobName = GridNode.getInstance()
.getJobExecutionService()
.getJobName(jobId);
activeJobId = jobId;
final long timeStart = System.currentTimeMillis();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
showBusyIcon();
setStatus("Executing Job");
((JLabel) getUIElement("general.stats.jobname"))
.setText(jobName);
((JLabel) getUIElement("general.stats.tasks")).setText("0");
((JLabel) getUIElement("general.stats.failures")).setText("0");
}
});
// Duration Update Thread
new Thread(new Runnable() {
@Override
public void run() {
while (jobId.equals(activeJobId)) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
((JLabel) getUIElement("general.stats.duration"))
.setText(TimeUtils.timeDifference(timeStart));
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.warn("Interrupted", e);
}
}
}
}).start();
// Job End Hook
ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
@Override
public void onServiceEvent(ServiceMessage message) {
activeJobId = null;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
JLabel durationLabel = getUIElement("general.stats.duration");
JLabel jobnameLabel = getUIElement("general.stats.jobname");
JLabel tasksLabel = getUIElement("general.stats.tasks");
JLabel failuresLabel = getUIElement("general.stats.failures");
JLabel totalJobsLabel = getUIElement("general.stats.totaljobs");
int tasks = Integer.parseInt(tasksLabel.getText());
int failures = Integer.parseInt(failuresLabel.getText());
// Create and enter Job History Element
JobHistoryElement element =
new JobHistoryElement(jobName,
jobId,
TimeUtils.formatDate(timeStart),
durationLabel.getText(),
tasks,
failures);
historyList.addJobHistoryElement(element);
// Update Job Info Fields
durationLabel.setText("N/A");
jobnameLabel.setText("N/A");
tasksLabel.setText("N/A");
failuresLabel.setText("N/A");
// Update Total Jobs Count
int totalJobs = Integer.parseInt(totalJobsLabel.getText()) + 1;
totalJobsLabel.setText(String.valueOf(totalJobs));
showIdleIcon();
setStatus("Idle");
} catch (Exception e) {
log.warn("[UI] Exception ",e);
}
}
});
}
}, jobId, ServiceMessageType.LOCAL_JOBFINISHED);
}
}, ServiceMessageType.LOCAL_JOBSTARTED);
// Task Executed Hook
ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
public void onServiceEvent(final ServiceMessage message) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Total Tasks
JLabel totalTasksLabel = getUIElement("general.stats.totaltasks");
int totalTasks = Integer.parseInt(totalTasksLabel.getText()) + 1;
totalTasksLabel.setText(String.valueOf(totalTasks));
// If active job, update task count
if (message.getMessage().equals(activeJobId)) {
JLabel tasksLabel = getUIElement("general.stats.tasks");
int tasks = Integer.parseInt(tasksLabel.getText()) + 1;
tasksLabel.setText(String.valueOf(tasks));
}
} catch (Exception e) {
log.warn("[UI] Exception ",e);
}
}
});
}
}, ServiceMessageType.LOCAL_TASKDONE);
// Task Failed Hook
ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
public void onServiceEvent(final ServiceMessage message) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// If active job, update task count
if (message.getMessage().equals(activeJobId)) {
JLabel failuresLabel = getUIElement("general.stats.failures");
int fails = Integer.parseInt(failuresLabel.getText()) + 1;
failuresLabel.setText(String.valueOf(fails));
}
} catch (Exception e) {
log.warn("[UI] Exception ",e);
}
}
});
}
}, ServiceMessageType.LOCAL_TASKFAILED);
// Task Execution Time Hook
ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
public void onServiceEvent(final ServiceMessage message) {
try {
executionTime += Long.parseLong(message.getMessage());
} catch (Exception e) {
log.warn("[UI] Exception ",e);
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updateExecutionTime();
}
});
}
}, ServiceMessageType.LOCAL_TASKEXEC);
// Node Banned Hook
ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
public void onServiceEvent(final ServiceMessage message) {
// If not relevant to node, ignore
if (!message.getMessage().startsWith(GridNode.getInstance().getId().toString())) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// If active job, update task count
JLabel bansLabel = getUIElement("general.stats.totalbans");
int bans = Integer.parseInt(bansLabel.getText()) + 1;
bansLabel.setText(String.valueOf(bans));
} catch (Exception e) {
log.warn("[UI] Exception ",e);
}
}
});
}
},ServiceMessageType.NODE_BANNED);
}
/**
* Updates the total execution time field
*/
private void updateExecutionTime() {
((JLabel) getUIElement("general.stats.exectime"))
.setText(TimeUtils.buildTimeString(executionTime));
}
/**
* Adds a Component to the components map of this object.
*
* @param identifier Component Identifier
* @param component Component
*/
protected void addUIElement(String identifier, JComponent component) {
components.put(identifier, component);
}
/**
* Removes a Component from the components map of this object.
*
* @param identifier Component Identifier
*/
protected void removeUIElement(String identifier) {
components.remove(identifier);
}
/**
* Returns the UI Element for given Identifier.
*
* @param <T> Expected Type of UI Element
* @param identifier Element Identifier
*
* @return UI Element Instance
*
* @throws IllegalArgumentException if invalid identifier
* @throws ClassCastException if invalid type
*/
@SuppressWarnings("unchecked")
protected <T extends JComponent> T getUIElement(String identifier) throws IllegalArgumentException, ClassCastException {
if (! components.containsKey(identifier)) throw new IllegalArgumentException("Invalid Identifier");
return (T) components.get(identifier);
}
/* -- ACTIONS --*/
/**
* Shutdowns the GridNode
*/
protected void doShutdownNode() {
int result = JOptionPane.showConfirmDialog(this, "Are You Sure to Shutdown Node ?","Nebula Grid", JOptionPane.YES_NO_OPTION);
// If user chose no, abort shutdown
if (result==JOptionPane.NO_OPTION) return;
new Thread(new Runnable() {
@Override
public void run() {
try {
// If connected to Cluster, unregister
if (Grid.isNode()) {
GridNode.getInstance().shutdown();
}
removeIcon();
System.exit(0);
} catch (Exception e) {
log.fatal("[GridNode] Exception while Shutting Down",e);
System.exit(1);
}
}
}).start();
}
/**
* Attempts to discover a cluster.
*
* @param silent if silent, no message will be displayed
*/
protected void doDiscover(final boolean silent) {
new Thread(new Runnable() {
public void run() {
synchronized (lastDiscoveryAttempt) {
try {
showBusyIcon();
getUIElement("menu.node.discover").setEnabled(false);
getUIElement("menu.node.autodiscover").setEnabled(false);
// Attempt start GridNode
Grid.startGridNode();
} catch ( DiscoveryFailureException e) {
if (!silent) {
JOptionPane.showMessageDialog(NodeMainUI.this, "Unable to discover any Cluster");
}
} catch (Exception e) {
if (!silent) {
JOptionPane.showMessageDialog(NodeMainUI.this, "Exception while attempting Cluster Connection");
}
log.error("[GridNode] Exception while connecting", e);
} finally {
getUIElement("menu.node.discover").setEnabled(true);
getUIElement("menu.node.autodiscover").setEnabled(true);
updateGridInfo();
showIdleIcon();
lastDiscoveryAttempt = System.currentTimeMillis();
}
}
}
}).start();
}
/**
* Displays About Dialog
*/
protected void showAbout() {
try {
new AboutDialog(this);
} catch (Exception e) {
log.error(e);
JOptionPane.showMessageDialog(this, "Unable to display About dialog");
}
}
/**
* Displays Help Contents
*/
protected void showHelp() {
UISupport.displayHelp(this);
}
/**
* Displays Configuration dialog
*/
protected void showConfiguration() {
// TODO Implement
JOptionPane.showMessageDialog(this, "This feature is not implemented");
}
/**
* Invoked on Shutdown Event
*/
protected void onShutdown() {
showBusyIcon();
doShutdownNode();
showIdleIcon();
}
/**
* Class which represents a Job History Element, inserted into the
* JobHistory List Model.
*
* @author Yohan Liyanage
* @version 1.0
*/
protected static class JobHistoryElement {
private String jobName;
private String jobId;
private String startTime;
private String duration;
private int tasks;
private int failures;
/**
* Constructs a JobHistoryElement with given field
* values.
*
* @param jobName job name
* @param jobId job id
* @param startTime job start time
* @param duration job duration
* @param tasks tasks executed for job on this node
* @param failures total failures for job on this node
*/
public JobHistoryElement(String jobName, String jobId,
String startTime, String duration, int tasks, int failures) {
super();
this.jobName = jobName;
this.jobId = jobId;
this.startTime = startTime;
this.duration = duration;
this.tasks = tasks;
this.failures = failures;
}
/**
* Returns the user friendly name for Job
*
* @return name
*/
public String getJobName() {
return jobName;
}
/**
* Returns the JobId for Job
* @return job id
*/
public String getJobId() {
return jobId;
}
/**
* Returns the start time for job
* @return start time
*/
public String getStartTime() {
return startTime;
}
/**
* Returns the duration for job
* @return duration
*/
public String getDuration() {
return duration;
}
/**
* Returns the number of tasks executed for job on this node
* @return tasks
*/
public int getTasks() {
return tasks;
}
/**
* Returns the number of tasks failed for job on this node
* @return tasks
*/
public int getFailures() {
return failures;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (!( obj instanceof JobHistoryElement)) return false;
JobHistoryElement element = (JobHistoryElement) obj;
return this.jobId.equals(element.getJobId());
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return this.jobId.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "[" + this.startTime + "] " + this.jobName;
}
}
/**
* ListModel for JobHistory List.
*
* @author Yohan Liyanage
* @version 1.0
*/
protected static class JobHistoryListModel extends AbstractListModel {
private static final long serialVersionUID = 4872462645102776860L;
// Job History Elements
private List<JobHistoryElement> elements = new ArrayList<JobHistoryElement>();
/**
* Adds a new JobHistoryElement.
*
* @param element new element
*/
public void addJobHistoryElement(JobHistoryElement element) {
elements.add(element);
fireIntervalAdded(this, this.getSize()-1, this.getSize()-1);
}
/**
* {@inheritDoc}
*/
@Override
public Object getElementAt(int index) {
return elements.get(index);
}
/**
* {@inheritDoc}
*/
@Override
public int getSize() {
return elements.size();
}
}
/**
* Displays Splash Screen.
*
* @return Splash Screen reference
*/
public static JWindow showSplash() {
JWindow splash = new JWindow();
splash.setSize(400, 250);
splash.setLayout(null);
JLabel status = new JLabel("Developed by Yohan Liyanage, 2008");
JLabelAppender.setLabel(status);
status.setFont(new Font("sansserif", Font.PLAIN, 10));
status.setSize(350, 30);
status.setLocation(10, 220);
splash.add(status);
JLabel lbl = new JLabel(new ImageIcon(ClassLoader.getSystemResource("META-INF/resources/nebula-startup.png")));
lbl.setSize(400, 250);
lbl.setLocation(0, 0);
splash.add(lbl);
splash.setVisible(true);
splash.setLocationRelativeTo(null);
return splash;
}
/**
* Creates a GridNode UI
* @return UI
*/
public static NodeMainUI create() {
final NodeMainUI ui = new NodeMainUI();
ui.setLocationRelativeTo(null);
ui.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
ui.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
ui.onShutdown();
}
});
return ui;
}
}