org.nebulaframework.ui.swing.cluster.ClusterMainUI.java Source code

Java tutorial

Introduction

Here is the source code for org.nebulaframework.ui.swing.cluster.ClusterMainUI.java

Source

/*
 * 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.cluster;

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
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.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nebulaframework.core.job.GridJob;
import org.nebulaframework.core.job.GridJobState;
import org.nebulaframework.core.job.GridJobStateListener;
import org.nebulaframework.core.job.splitaggregate.SplitAggregateGridJob;
import org.nebulaframework.core.job.unbounded.UnboundedGridJob;
import org.nebulaframework.grid.Grid;
import org.nebulaframework.grid.cluster.manager.ClusterManager;
import org.nebulaframework.grid.cluster.manager.services.jobs.GridJobProfile;
import org.nebulaframework.grid.cluster.manager.services.jobs.InternalClusterJobService;
import org.nebulaframework.grid.cluster.node.delegate.GridNodeDelegate;
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.profiling.TimeUtils;
import org.springframework.util.StringUtils;

/**
 * The Swing UI for the ClusterManager.
 * This UI exposes basic functionality of ClusterManager,
 * and allows users to easily manage the Cluster.
 * <p>
 * However, for more advanced uses, such as embedding
 * ClusterManager, consider using the API.
 * 
 * @author Yohan Liyanage
 * @version 1.0
 */
public class ClusterMainUI extends JFrame {

    private static Log log = LogFactory.getLog(ClusterMainUI.class);

    private static final int WIDTH = 600;
    private static final int HEIGHT = 475;

    private static final long serialVersionUID = 8992643609753054554L;
    private static Map<String, JComponent> components = new HashMap<String, JComponent>();

    private TrayIcon trayIcon;

    private Image idleIcon;
    private Image activeIcon;

    /**
     * Constructs the UI for ClusterManager.
     * Note that to construct the UI, the Grid should be
     * started and ClusterManager should be running.
     * 
     * @throws HeadlessException if UI not supported
     * @throws IllegalStateException if ClusterManager is not running
     */
    public ClusterMainUI() throws HeadlessException, IllegalStateException {
        super();

        if (!Grid.isClusterManager()) {
            throw new IllegalStateException("ClusterManager is not running");
        }
        setupUI();
        showClusterInfo();
    }

    /**
     * Primary UI Setup Operations
     */
    private void setupUI() {

        setTitle("Nebula Grid - Cluster Manager");
        setSize(WIDTH, HEIGHT);

        /* -- Menu Bar -- */
        setJMenuBar(setupMenu());

        /* -- Content -- */

        setLayout(new BorderLayout());

        JPanel centerPanel = new JPanel();

        add(centerPanel, BorderLayout.CENTER);

        /* -- Setup Tabs -- */
        centerPanel.setLayout(new BorderLayout());
        JTabbedPane tabs = new JTabbedPane();
        tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
        centerPanel.add(tabs);
        addUIElement("tabs", tabs); // Add to components map

        // General Tab
        tabs.addTab("General", setupGeneralTab());

        setupTrayIcon(this);

        // Create Job Start Hook
        ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
            public void onServiceEvent(final ServiceMessage message) {
                createJobTab(message.getMessage());
            }
        }, ServiceMessageType.JOB_START);
    }

    /**
     * System Tray Icon setup
     * @param frame owner JFrame
     */
    private void setupTrayIcon(final JFrame frame) {

        // Idle Icon
        idleIcon = Toolkit.getDefaultToolkit()
                .getImage(ClassLoader.getSystemResource("META-INF/resources/cluster_inactive.png"));

        // Active Icon
        activeIcon = Toolkit.getDefaultToolkit()
                .getImage(ClassLoader.getSystemResource("META-INF/resources/cluster_active.png"));

        frame.setIconImage(idleIcon);

        // If system tray is supported by OS
        if (SystemTray.isSupported()) {

            // Set Icon
            trayIcon = new TrayIcon(idleIcon, "Nebula Grid Cluster", 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);
                }

            });
        }

    }

    /**
     * Creates the Pop-up menu for System Tray Icon
     * 
     * @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
        MenuItem shutdownItem = new MenuItem("Shutdown");
        shutdownItem.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                doShutdownCluster();
            }

        });
        trayPopup.add(shutdownItem);

        return trayPopup;
    }

    /**
     * Displays the Busy Icon on SystemTray
     */
    private void showBusyIcon() {
        if (trayIcon != null)
            trayIcon.setImage(activeIcon);
    }

    /**
     * Displays the Idle icon on SystemTray
     */
    private void showIdleIcon() {
        if (trayIcon != null)
            trayIcon.setImage(idleIcon);
    }

    /**
     * Removes System Tray icon.
     */
    private void removeIcon() {
        if (SystemTray.isSupported()) {
            SystemTray.getSystemTray().remove(trayIcon);
        }
    }

    /**
     * Setups the Menu Bar
     * @return
     */
    private JMenuBar setupMenu() {
        JMenuBar menuBar = new JMenuBar();

        /* -- Cluster Menu -- */
        JMenu clusterMenu = new JMenu("Cluster");
        clusterMenu.setMnemonic(KeyEvent.VK_C);
        menuBar.add(clusterMenu);

        // Discover Submenu
        JMenu clusterDiscoverMenu = new JMenu("Disover Peers");
        clusterDiscoverMenu.setMnemonic(KeyEvent.VK_D);
        clusterMenu.add(clusterDiscoverMenu);

        // Discover -> Multicast
        JMenuItem clusterDiscoverMulticast = new JMenuItem("Multicast");
        clusterDiscoverMulticast.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0));
        clusterDiscoverMulticast.setEnabled(false);
        clusterDiscoverMulticast.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doDiscoverMulticast();
            }
        });
        clusterDiscoverMenu.add(clusterDiscoverMulticast);
        addUIElement("menu.cluster.discover.multicast", clusterDiscoverMulticast); // Add to components map

        // Discover -> WS
        JMenuItem clusterDiscoverWS = new JMenuItem("Colombus Web Service");
        clusterDiscoverWS.setEnabled(false);
        clusterDiscoverWS.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0));
        clusterDiscoverWS.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doDiscoverWS();
            }
        });
        clusterDiscoverMenu.add(clusterDiscoverWS);
        addUIElement("menu.cluster.discover.ws", clusterDiscoverWS); // Add to components map

        clusterMenu.addSeparator();

        // Cluster-> Shutdown
        JMenuItem clusterShutdownItem = new JMenuItem("Shutdown", 'u');
        clusterShutdownItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0));
        clusterShutdownItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doShutdownCluster();
            }
        });
        clusterMenu.add(clusterShutdownItem);
        addUIElement("menu.cluster.shutdown", clusterShutdownItem); // 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 the General Tab Pane
     * 
     * @return JPanel for pane
     */
    private JPanel setupGeneralTab() {

        JPanel generalTab = new JPanel();
        generalTab.setLayout(new BorderLayout());

        /* -- Create Main Panels -- */

        JPanel centerPanel = new JPanel();
        JPanel northPanel = new JPanel();
        JPanel southPanel = new JPanel();

        generalTab.add(centerPanel, BorderLayout.CENTER);
        generalTab.add(northPanel, BorderLayout.NORTH);

        /* -- Create Center Contents -- */

        // Statistics Panel
        JPanel statsPanel = setupStatsPanel();

        // Log Panel
        JPanel logPanel = new JPanel();
        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);

        centerPanel.setLayout(new BorderLayout(10, 10));
        centerPanel.add(statsPanel, BorderLayout.NORTH);
        centerPanel.add(logPanel, BorderLayout.CENTER);

        /* -- Create Buttons (South) -- */

        generalTab.add(southPanel, BorderLayout.SOUTH);

        final JButton shutdownButton = new JButton("Shutdown");
        shutdownButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doShutdownCluster();
            }
        });

        southPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 10));
        southPanel.add(shutdownButton);
        addUIElement("general.shutdown", shutdownButton); // Add to components map

        return generalTab;
    }

    /**
     * Setup the Statistics Panel of General Pane
     * 
     * @return JPanel for Stats
     */
    private JPanel setupStatsPanel() {

        JPanel statPanel = new JPanel();
        statPanel.setLayout(new GridLayout(0, 2, 10, 10));

        JPanel clusterInfoPanel = new JPanel();
        clusterInfoPanel.setLayout(new GridLayout(0, 2, 10, 10));
        clusterInfoPanel.setBorder(BorderFactory.createTitledBorder("Cluster Information"));
        statPanel.add(clusterInfoPanel);

        JPanel gridInfoPanel = new JPanel();
        gridInfoPanel.setLayout(new GridLayout(0, 2, 10, 10));
        gridInfoPanel.setBorder(BorderFactory.createTitledBorder("Grid Information"));
        statPanel.add(gridInfoPanel);

        /* -- Cluster Information -- */

        // ClusterID
        JLabel clusterIDLabel = new JLabel("Cluster ID :");
        clusterInfoPanel.add(clusterIDLabel);
        JLabel clusterID = new JLabel("#clusterId#");
        clusterInfoPanel.add(clusterID);
        addUIElement("general.stats.clusterid", clusterID); // Add to components map

        // Host Information (ex. localhost:61616)
        JLabel hostInfoLabel = new JLabel("Host Information :");
        clusterInfoPanel.add(hostInfoLabel);
        JLabel hostInfo = new JLabel("#hostInfo#");
        clusterInfoPanel.add(hostInfo);
        addUIElement("general.stats.hostinfo", hostInfo); // Add to components map

        // Protocol Information
        JLabel protocolsLabel = new JLabel("Protocols :");
        clusterInfoPanel.add(protocolsLabel);
        JLabel protocols = new JLabel("#protocols#");
        clusterInfoPanel.add(protocols);
        addUIElement("general.stats.protocols", protocols); // Add to components map

        // Cluster Up Time
        JLabel upTimeLabel = new JLabel("Cluster Up Time :");
        clusterInfoPanel.add(upTimeLabel);
        JLabel upTime = new JLabel("#upTime#");
        clusterInfoPanel.add(upTime);
        addUIElement("general.stats.uptime", upTime); // Add to components map

        /* -- Grid Information -- */

        // Peer Cluster Count
        JLabel peerClustersLabel = new JLabel("Peer Clusters :");
        gridInfoPanel.add(peerClustersLabel);
        JLabel peerClusters = new JLabel("#peerClusters#");
        gridInfoPanel.add(peerClusters);
        addUIElement("general.stats.peerclusters", peerClusters); // Add to components map

        // Node Count
        JLabel nodesLabel = new JLabel("Nodes in Cluster :");
        gridInfoPanel.add(nodesLabel);
        JLabel nodes = new JLabel("#nodes#");
        gridInfoPanel.add(nodes);
        addUIElement("general.stats.nodes", nodes); // Add to components map

        // Jobs Done Count
        JLabel jobsDoneLabel = new JLabel("Executed Jobs :");
        gridInfoPanel.add(jobsDoneLabel);
        JLabel jobsDone = new JLabel("#jobsdone#");
        gridInfoPanel.add(jobsDone);
        addUIElement("general.stats.jobsdone", jobsDone); // Add to components map

        // Active Jobs
        JLabel activeJobsLabel = new JLabel("Active Jobs :");
        gridInfoPanel.add(activeJobsLabel);
        JLabel activeJobs = new JLabel("#activeJobs#");
        gridInfoPanel.add(activeJobs);
        addUIElement("general.stats.activejobs", activeJobs); // Add to components map

        return statPanel;
    }

    /**
     * Creates a tab pane for the given GridJob.
     * 
     * @param jobId JobId
     */
    protected void createJobTab(final String jobId) {

        // Request Job Profile
        final InternalClusterJobService jobService = ClusterManager.getInstance().getJobService();
        final GridJobProfile profile = jobService.getProfile(jobId);

        // Job Start Time
        final long startTime = System.currentTimeMillis();

        final JPanel jobPanel = new JPanel();
        jobPanel.setLayout(new BorderLayout(10, 10));

        // Progess Panel
        JPanel progressPanel = new JPanel();
        progressPanel.setLayout(new BorderLayout(10, 10));
        progressPanel.setBorder(BorderFactory.createTitledBorder("Progress"));
        jobPanel.add(progressPanel, BorderLayout.NORTH);

        final JProgressBar progressBar = new JProgressBar();
        progressBar.setStringPainted(true);
        progressPanel.add(progressBar, BorderLayout.CENTER);
        addUIElement("jobs." + jobId + ".progress", progressBar); // Add to components map

        // Buttons Panel
        JPanel buttonsPanel = new JPanel();
        jobPanel.add(buttonsPanel, BorderLayout.SOUTH);
        buttonsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));

        // Terminate Button
        JButton terminateButton = new JButton("Terminate");
        terminateButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new Thread(new Runnable() {

                    public void run() {

                        // Job Name = Class Name
                        String name = profile.getJob().getClass().getSimpleName();

                        // Request user confirmation
                        int option = JOptionPane.showConfirmDialog(ClusterMainUI.this,
                                "Are you sure to terminate GridJob " + name + "?", "Nebula - Terminate GridJob",
                                JOptionPane.YES_NO_OPTION);

                        if (option == JOptionPane.NO_OPTION)
                            return;

                        // Attempt Cancel
                        boolean result = profile.getFuture().cancel();

                        // Notify results
                        if (result) {
                            JOptionPane.showMessageDialog(ClusterMainUI.this,
                                    "Grid Job '" + name + "terminated successfully.", "Nebula - Job Terminated",
                                    JOptionPane.INFORMATION_MESSAGE);
                        } else {
                            JOptionPane.showMessageDialog(ClusterMainUI.this,
                                    "Failed to terminate Grid Job '" + name, "Nebula - Job Termination Failed",
                                    JOptionPane.WARNING_MESSAGE);
                        }
                    }

                }).start();
            }
        });
        buttonsPanel.add(terminateButton);
        addUIElement("jobs." + jobId + ".terminate", terminateButton); // Add to components map

        // Close Tab Button
        JButton closeButton = new JButton("Close Tab");
        closeButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        removeJobTab(jobId);
                    }
                });
            }
        });
        closeButton.setEnabled(false);

        buttonsPanel.add(closeButton);
        addUIElement("jobs." + jobId + ".closetab", closeButton); // Add to components map

        JPanel centerPanel = new JPanel();
        centerPanel.setLayout(new GridBagLayout());
        jobPanel.add(centerPanel, BorderLayout.CENTER);

        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.weightx = 1.0;

        /* -- Job Information -- */

        JPanel jobInfoPanel = new JPanel();
        jobInfoPanel.setBorder(BorderFactory.createTitledBorder("Job Information"));
        jobInfoPanel.setLayout(new GridBagLayout());
        c.gridy = 0;
        c.ipady = 30;
        centerPanel.add(jobInfoPanel, c);

        GridBagConstraints c1 = new GridBagConstraints();
        c1.fill = GridBagConstraints.BOTH;
        c1.weightx = 1;
        c1.weighty = 1;

        // Name
        jobInfoPanel.add(new JLabel("Name :"), c1);
        JLabel jobNameLabel = new JLabel();
        jobInfoPanel.add(jobNameLabel, c1);
        jobNameLabel.setText(profile.getJob().getClass().getSimpleName());
        addUIElement("jobs." + jobId + ".job.name", jobNameLabel); // Add to components map

        // Gap
        jobInfoPanel.add(new JLabel(), c1);

        // Type
        jobInfoPanel.add(new JLabel("Type :"), c1);
        JLabel jobType = new JLabel();
        jobType.setText(getJobType(profile.getJob()));
        jobInfoPanel.add(jobType, c1);
        addUIElement("jobs." + jobId + ".job.type", jobType); // Add to components map

        // Job Class Name
        c1.gridy = 1;
        c1.gridwidth = 1;
        jobInfoPanel.add(new JLabel("GridJob Class :"), c1);
        c1.gridwidth = GridBagConstraints.REMAINDER;
        JLabel jobClassLabel = new JLabel();
        jobClassLabel.setText(profile.getJob().getClass().getName());
        jobInfoPanel.add(jobClassLabel, c1);
        addUIElement("jobs." + jobId + ".job.class", jobClassLabel); // Add to components map

        /* -- Execution Information -- */

        JPanel executionInfoPanel = new JPanel();
        executionInfoPanel.setBorder(BorderFactory.createTitledBorder("Execution Statistics"));
        executionInfoPanel.setLayout(new GridBagLayout());
        c.gridy = 1;
        c.ipady = 30;
        centerPanel.add(executionInfoPanel, c);

        GridBagConstraints c3 = new GridBagConstraints();
        c3.weightx = 1;
        c3.weighty = 1;
        c3.fill = GridBagConstraints.BOTH;

        // Start Time
        executionInfoPanel.add(new JLabel("Job Status :"), c3);
        final JLabel statusLabel = new JLabel("Initializing");
        executionInfoPanel.add(statusLabel, c3);
        addUIElement("jobs." + jobId + ".execution.status", statusLabel); // Add to components map

        // Status Update Listener
        profile.getFuture().addGridJobStateListener(new GridJobStateListener() {
            public void stateChanged(final GridJobState newState) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        statusLabel.setText(StringUtils.capitalize(newState.toString().toLowerCase()));
                    }
                });
            }
        });

        executionInfoPanel.add(new JLabel(), c3); // Space Holder

        // Percent Complete
        executionInfoPanel.add(new JLabel("Completed % :"), c3);
        final JLabel percentLabel = new JLabel("-N/A-");
        executionInfoPanel.add(percentLabel, c3);
        addUIElement("jobs." + jobId + ".execution.percentage", percentLabel); // Add to components map

        c3.gridy = 1;

        // Start Time
        executionInfoPanel.add(new JLabel("Start Time :"), c3);
        JLabel startTimeLabel = new JLabel(DateFormat.getInstance().format(new Date(startTime)));
        executionInfoPanel.add(startTimeLabel, c3);
        addUIElement("jobs." + jobId + ".execution.starttime", startTimeLabel); // Add to components map

        executionInfoPanel.add(new JLabel(), c3); // Space Holder

        // Elapsed Time
        executionInfoPanel.add(new JLabel("Elapsed Time :"), c3);
        JLabel elapsedTimeLabel = new JLabel("-N/A-");
        executionInfoPanel.add(elapsedTimeLabel, c3);
        addUIElement("jobs." + jobId + ".execution.elapsedtime", elapsedTimeLabel); // Add to components map

        c3.gridy = 2;

        // Tasks Deployed (Count)
        executionInfoPanel.add(new JLabel("Tasks Deployed :"), c3);
        JLabel tasksDeployedLabel = new JLabel("-N/A-");
        executionInfoPanel.add(tasksDeployedLabel, c3);
        addUIElement("jobs." + jobId + ".execution.tasks", tasksDeployedLabel); // Add to components map

        executionInfoPanel.add(new JLabel(), c3); // Space Holder

        // Results Collected (Count)
        executionInfoPanel.add(new JLabel("Results Collected :"), c3);
        JLabel resultsCollectedLabel = new JLabel("-N/A-");
        executionInfoPanel.add(resultsCollectedLabel, c3);
        addUIElement("jobs." + jobId + ".execution.results", resultsCollectedLabel); // Add to components map

        c3.gridy = 3;

        // Remaining Tasks (Count)
        executionInfoPanel.add(new JLabel("Remaining Tasks :"), c3);
        JLabel remainingTasksLabel = new JLabel("-N/A-");
        executionInfoPanel.add(remainingTasksLabel, c3);
        addUIElement("jobs." + jobId + ".execution.remaining", remainingTasksLabel); // Add to components map

        executionInfoPanel.add(new JLabel(), c3); // Space Holder

        // Failed Tasks (Count)
        executionInfoPanel.add(new JLabel("Failed Tasks :"), c3);
        JLabel failedTasksLabel = new JLabel("-N/A-");
        executionInfoPanel.add(failedTasksLabel, c3);
        addUIElement("jobs." + jobId + ".execution.failed", failedTasksLabel); // Add to components map

        /* -- Submitter Information -- */
        UUID ownerId = profile.getOwner();
        GridNodeDelegate owner = ClusterManager.getInstance().getClusterRegistrationService()
                .getGridNodeDelegate(ownerId);

        JPanel ownerInfoPanel = new JPanel();
        ownerInfoPanel.setBorder(BorderFactory.createTitledBorder("Owner Information"));
        ownerInfoPanel.setLayout(new GridBagLayout());
        c.gridy = 2;
        c.ipady = 10;
        centerPanel.add(ownerInfoPanel, c);

        GridBagConstraints c2 = new GridBagConstraints();

        c2.fill = GridBagConstraints.BOTH;
        c2.weightx = 1;
        c2.weighty = 1;

        // Host Name
        ownerInfoPanel.add(new JLabel("Host Name :"), c2);
        JLabel hostNameLabel = new JLabel(owner.getProfile().getName());
        ownerInfoPanel.add(hostNameLabel, c2);
        addUIElement("jobs." + jobId + ".owner.hostname", hostNameLabel); // Add to components map

        // Gap
        ownerInfoPanel.add(new JLabel(), c2);

        // Host IP Address
        ownerInfoPanel.add(new JLabel("Host IP :"), c2);
        JLabel hostIPLabel = new JLabel(owner.getProfile().getIpAddress());
        ownerInfoPanel.add(hostIPLabel, c2);
        addUIElement("jobs." + jobId + ".owner.hostip", hostIPLabel); // Add to components map

        // Owner UUID
        c2.gridy = 1;
        c2.gridx = 0;
        ownerInfoPanel.add(new JLabel("Owner ID :"), c2);
        JLabel ownerIdLabel = new JLabel(profile.getOwner().toString());
        c2.gridx = 1;
        c2.gridwidth = 4;
        ownerInfoPanel.add(ownerIdLabel, c2);
        addUIElement("jobs." + jobId + ".owner.id", ownerIdLabel); // Add to components map

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // Create Tab
                addUIElement("jobs." + jobId, jobPanel);
                JTabbedPane tabs = getUIElement("tabs");
                tabs.addTab(profile.getJob().getClass().getSimpleName(), jobPanel);
                tabs.revalidate();
            }
        });

        // Execution Information Updater Thread
        new Thread(new Runnable() {

            boolean initialized = false;
            boolean unbounded = false;

            public void run() {

                // Unbounded, No Progress Supported
                if ((!initialized) && profile.getJob() instanceof UnboundedGridJob<?>) {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            progressBar.setIndeterminate(true);
                            progressBar.setStringPainted(false);

                            // Infinity Symbol
                            percentLabel.setText(String.valueOf('\u221e'));

                        }
                    });
                    initialized = true;
                    unbounded = true;
                }

                // Update Job Info
                while (true) {

                    try {
                        // 500ms Interval
                        Thread.sleep(500);

                    } catch (InterruptedException e) {
                        log.warn("Interrupted Progress Updater Thread", e);
                    }

                    final int totalCount = profile.getTotalTasks();
                    final int tasksRem = profile.getTaskCount();
                    final int resCount = profile.getResultCount();
                    final int failCount = profile.getFailedCount();

                    showBusyIcon();

                    // Task Information
                    JLabel totalTaskLabel = getUIElement("jobs." + jobId + ".execution.tasks");
                    totalTaskLabel.setText(String.valueOf(totalCount));

                    // Result Count
                    JLabel resCountLabel = getUIElement("jobs." + jobId + ".execution.results");
                    resCountLabel.setText(String.valueOf(resCount));

                    // Remaining Task Count
                    JLabel remLabel = getUIElement("jobs." + jobId + ".execution.remaining");
                    remLabel.setText(String.valueOf(tasksRem));

                    // Failed Task Count
                    JLabel failedLabel = getUIElement("jobs." + jobId + ".execution.failed");
                    failedLabel.setText(String.valueOf(failCount));

                    // Elapsed Time
                    JLabel elapsedLabel = getUIElement("jobs." + jobId + ".execution.elapsedtime");
                    elapsedLabel.setText(TimeUtils.timeDifference(startTime));

                    // Job State
                    final JLabel statusLabel = getUIElement("jobs." + jobId + ".execution.status");

                    // If not in Executing Mode
                    if ((!profile.getFuture().isJobFinished())
                            && profile.getFuture().getState() != GridJobState.EXECUTING) {
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {

                                // Progress Bar
                                progressBar.setIndeterminate(true);
                                progressBar.setStringPainted(false);

                                // Status Text
                                String state = profile.getFuture().getState().toString();
                                statusLabel.setText(StringUtils.capitalize(state.toLowerCase()));

                                // Percentage Label
                                percentLabel.setText(String.valueOf('\u221e'));
                            }
                        });
                    } else { // Executing Mode : Progress Information

                        // Job Finished, Stop
                        if (profile.getFuture().isJobFinished()) {
                            showIdleIcon();
                            return;
                        }

                        // Double check for status label
                        if (!statusLabel.getText().equalsIgnoreCase("executing")) {
                            SwingUtilities.invokeLater(new Runnable() {

                                @Override
                                public void run() {
                                    String newstate = profile.getFuture().getState().toString();
                                    statusLabel.setText(StringUtils.capitalize(newstate.toLowerCase()));
                                }

                            });
                        }

                        if (!unbounded) {

                            final int percentage = (int) (profile.percentage() * 100);

                            //final int failCount = profile.get
                            SwingUtilities.invokeLater(new Runnable() {
                                public void run() {

                                    // If finished at this point, do not update
                                    if (progressBar.getValue() == 100) {
                                        return;
                                    }

                                    // If ProgressBar is in indeterminate
                                    if (progressBar.isIndeterminate()) {
                                        progressBar.setIndeterminate(false);
                                        progressBar.setStringPainted(true);
                                    }

                                    // Update Progress Bar / Percent Label
                                    progressBar.setValue(percentage);
                                    percentLabel.setText(percentage + " %");

                                }
                            });
                        }
                    }

                }
            }
        }).start();

        // Job End Hook to Execute Job End Actions
        ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {

            public void onServiceEvent(final ServiceMessage event) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {

                        JButton close = getUIElement("jobs." + jobId + ".closetab");
                        JButton terminate = getUIElement("jobs." + jobId + ".terminate");
                        terminate.setEnabled(false);
                        close.setEnabled(true);

                        JProgressBar progress = getUIElement("jobs." + jobId + ".progress");
                        JLabel percentage = getUIElement("jobs." + jobId + ".execution.percentage");

                        progress.setEnabled(false);

                        // If Successfully Finished
                        if (event.getType() == ServiceMessageType.JOB_END) {

                            if (profile.getFuture().getState() != GridJobState.FAILED) {
                                // If Not Job Failed
                                progress.setValue(100);
                                percentage.setText("100 %");
                            } else {
                                // If Failed
                                percentage.setText("N/A");
                            }

                            // Stop (if) Indeterminate Progress Bar
                            if (progress.isIndeterminate()) {
                                progress.setIndeterminate(false);
                                progress.setStringPainted(true);
                            }
                        } else if (event.getType() == ServiceMessageType.JOB_CANCEL) {

                            if (progress.isIndeterminate()) {
                                progress.setIndeterminate(false);
                                progress.setStringPainted(false);
                                percentage.setText("N/A");
                            }
                        }

                        showIdleIcon();
                    }
                });
            }

        }, jobId, ServiceMessageType.JOB_CANCEL, ServiceMessageType.JOB_END);
    }

    /**
     * Returns the Job Type Name for given Job
     * 
     * @param job Job Instance
     * 
     * @return String representation of Job Type
     */
    private String getJobType(GridJob<?, ?> job) {
        if (job instanceof SplitAggregateGridJob<?, ?>) {
            return "Split-Aggregate";
        } else if (job instanceof UnboundedGridJob<?>) {
            return "Unbounded";
        } else {
            return "Unknown";
        }
    }

    /**
     * Removes the given Job Tab Pane
     * @param jobId JobId of pane
     */
    protected void removeJobTab(String jobId) {

        // Detach Tab
        JTabbedPane tabs = getUIElement("tabs");
        tabs.remove(getUIElement("jobs." + jobId));
        tabs.revalidate();

        // Remove UI Elements
        removeUIElement("jobs." + jobId + ".progress");
        removeUIElement("jobs." + jobId + ".terminate");
        removeUIElement("jobs." + jobId + ".closetab");
        removeUIElement("jobs." + jobId + ".job.name");
        removeUIElement("jobs." + jobId + ".job.type");
        removeUIElement("jobs." + jobId + ".job.class");
        removeUIElement("jobs." + jobId + ".execution.status");
        removeUIElement("jobs." + jobId + ".execution.percentage");
        removeUIElement("jobs." + jobId + ".execution.starttime");
        removeUIElement("jobs." + jobId + ".execution.elapsedtime");
        removeUIElement("jobs." + jobId + ".execution.tasks");
        removeUIElement("jobs." + jobId + ".execution.results");
        removeUIElement("jobs." + jobId + ".execution.remaining");
        removeUIElement("jobs." + jobId + ".execution.failed");
        removeUIElement("jobs." + jobId + ".owner.hostname");
        removeUIElement("jobs." + jobId + ".owner.hostip");
        removeUIElement("jobs." + jobId + ".owner.id");
        removeUIElement("jobs." + jobId);
    }

    /**
     * Invokes the Cluster Shutdown Operation
     */
    protected void doShutdownCluster() {

        // Consider different messages when there are active jobs
        String message = "Are you sure to shutdown the cluster ?";

        int response = JOptionPane.showConfirmDialog(this, message, "Shutdown Cluster", JOptionPane.YES_NO_OPTION);

        // User chose 'No'
        if (response == JOptionPane.NO_OPTION)
            return;
        JButton shutdownButton = getUIElement("general.shutdown");
        shutdownButton.setEnabled(false);

        showBusyIcon();

        // Shutdown
        onShutdown();
    }

    /**
     * Displays About Dialog Box
     */
    protected void showAbout() {
        new AboutDialog(this);
    }

    /**
     * Displays Help Contents
     */
    protected void showHelp() {
        UISupport.displayHelp(this);
    }

    /**
     * Invokes Peer Discovery using Colombus Web Servers.
     */
    protected void doDiscoverWS() {
        // TODO Implement
        JOptionPane.showMessageDialog(this, "This feature is not implemented");
    }

    /**
     * Invokes Peer Discovery using Multicast.
     */
    protected void doDiscoverMulticast() {
        // TODO Implement
        JOptionPane.showMessageDialog(this, "This feature is not implemented");
    }

    /**
     * Displays Configuration Dialog Box.
     */
    protected void showConfiguration() {
        // TODO Implement
        JOptionPane.showMessageDialog(this, "This feature is not implemented");
    }

    /**
     * Requests Cluster Shutdown.
     */
    public void onShutdown() {

        removeIcon();
        new Thread(new Runnable() {

            @Override
            public void run() {
                // Forced Shutdown

                ClusterManager.getInstance().shutdown(true);
            }

        }).start();
    }

    /**
     * Updates UI and displays the Cluster Information.
     */
    private void showClusterInfo() {
        ClusterManager mgr = ClusterManager.getInstance();

        // ClusterID
        final JLabel clusterId = getUIElement("general.stats.clusterid");
        clusterId.setText(mgr.getClusterId().toString());

        // HostInfo
        JLabel hostInfo = getUIElement("general.stats.hostinfo");
        hostInfo.setText(mgr.getClusterInfo().getHostInfo());

        // Protocols
        JLabel protocols = getUIElement("general.stats.protocols");
        protocols.setText(mgr.getClusterInfo().getProtocolInfo());

        // Uptime Initial Value
        JLabel upTime = getUIElement("general.stats.uptime");
        upTime.setText("");

        // Peer Clusters
        final JLabel clusters = getUIElement("general.stats.peerclusters");
        clusters.setText("0");

        // Peer Clusters Update Hook
        ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
            public void onServiceEvent(ServiceMessage message) {

                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        try {
                            int peers = ClusterManager.getInstance().getPeerService().getPeerCount();
                            clusters.setText(String.valueOf(peers));
                        } catch (Exception e) {
                            log.warn("[UI] Exception while accessing peer information", e);
                        }
                    }
                });
            }
        }, ServiceMessageType.PEER_CONNECTION, ServiceMessageType.PEER_DISCONNECTION);

        // Local Nodes
        final JLabel nodes = getUIElement("general.stats.nodes");
        nodes.setText(String.valueOf(ClusterManager.getInstance().getClusterRegistrationService().getNodeCount()));

        // Local Nodes Update Hook
        ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
            public void onServiceEvent(ServiceMessage message) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        int count = ClusterManager.getInstance().getClusterRegistrationService().getNodeCount();
                        nodes.setText(String.valueOf(count));
                    }
                });
            }
        }, ServiceMessageType.NODE_REGISTERED, ServiceMessageType.NODE_UNREGISTERED);

        // Completed Jobs
        final JLabel jobsdone = getUIElement("general.stats.jobsdone");
        jobsdone.setText("0");

        // Completed Jobs Update Hook
        ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
            public void onServiceEvent(ServiceMessage message) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        int count = ClusterManager.getInstance().getJobService().getFinishedJobCount();
                        jobsdone.setText(String.valueOf(count));
                    }
                });
            }
        }, ServiceMessageType.JOB_END, ServiceMessageType.JOB_CANCEL);

        // Active Jobs
        final JLabel activejobs = getUIElement("general.stats.activejobs");
        activejobs.setText(String.valueOf(ClusterManager.getInstance().getJobService().getActiveJobCount()));

        // Active Jobs Update Hook
        ServiceEventsSupport.addServiceHook(new ServiceHookCallback() {
            public void onServiceEvent(ServiceMessage message) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        int count = ClusterManager.getInstance().getJobService().getActiveJobCount();
                        activejobs.setText(String.valueOf(count));
                    }
                });
            }
        }, ServiceMessageType.JOB_START, ServiceMessageType.JOB_CANCEL, ServiceMessageType.JOB_END);

        // 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();
    }

    /**
     * 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);
    }

    /**
     * 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 the UI for ClusterManager,
     * and returns.
     * 
     * @return UI reference
     */
    public static ClusterMainUI create() {
        final ClusterMainUI ui = new ClusterMainUI();
        ui.setLocationRelativeTo(null);

        ui.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        // Close Handler
        ui.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                ui.doShutdownCluster();
            }

        });

        return ui;
    }

}