com.amazonaws.eclipse.ec2.ui.views.instances.InstanceSelectionTable.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.eclipse.ec2.ui.views.instances.InstanceSelectionTable.java

Source

/*
 * Copyright 2009-2011 Amazon Technologies, Inc.
 *
 * 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://aws.amazon.com/apache2.0
 *
 * This file 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 com.amazonaws.eclipse.ec2.ui.views.instances;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.statushandlers.StatusManager;

import com.amazonaws.eclipse.core.AccountInfo;
import com.amazonaws.eclipse.core.AwsToolkitCore;
import com.amazonaws.eclipse.core.ui.IRefreshable;
import com.amazonaws.eclipse.ec2.Ec2Plugin;
import com.amazonaws.eclipse.ec2.InstanceType;
import com.amazonaws.eclipse.ec2.keypairs.KeyPairManager;
import com.amazonaws.eclipse.ec2.ui.SelectionTable;
import com.amazonaws.eclipse.ec2.ui.ebs.CreateNewVolumeDialog;
import com.amazonaws.eclipse.ec2.utils.DynamicMenuAction;
import com.amazonaws.eclipse.ec2.utils.IMenu;
import com.amazonaws.eclipse.ec2.utils.MenuAction;
import com.amazonaws.eclipse.ec2.utils.MenuHandler;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.Reservation;

/**
 * Table displaying EC2 instances and a context menu with actions like opening
 * remote shells, terminating instances, rebooting instances, attaching EBS
 * volumes, etc.
 */
public class InstanceSelectionTable extends SelectionTable implements IRefreshable, IMenu {

    /* Menu Actions */
    private Action refreshAction;
    private Action rebootAction;
    private Action terminateAction;
    private Action openShellAction;
    private Action openShellDialogAction;
    private Action createAmiAction;
    private Action copyPublicDnsNameAction;
    private Action attachNewVolumeAction;
    private Action startInstancesAction;
    private Action stopInstancesAction;

    /** Dropdown filter menu for Instance State */
    private IAction instanceStateFilterDropDownAction;

    /** DropDown menu handler for Instance State */
    private MenuHandler instanceStateDropDownMenuHandler;

    /** Dropdown filter menu for Security Group*/
    private IAction securityGroupFilterDropDownAction;

    /** DropDown menu handler for Security Group */
    private MenuHandler securityGroupDropDownMenuHandler;

    /** Holds the ALL option for Security Group Filter Item */
    private MenuItem allSecurityGroupFilterItem;

    /** The timer we use to have this table automatically refreshed */
    private RefreshTimer refreshInstanceListTimer;

    /** Shared account info */
    final static AccountInfo accountInfo = AwsToolkitCore.getDefault().getAccountInfo();

    /** Content and label provider for this selection table */
    ViewContentAndLabelProvider contentAndLabelProvider;

    private KeyPairManager keyPairManager = new KeyPairManager();

    /**
     * An optional field containing a list of Amazon EC2 instances to display in
     * this selection table
     */
    private List<String> instancesToDisplay;

    /** Stores the no of instances that are displayed */
    private int noOfInstances;

    /*
     * Public Interface
     */

    /**
     * Creates a new instance selection table within the specified composite.
     *
     * @param parent
     *            The parent composite for this new instance selection table.
     */
    public InstanceSelectionTable(Composite parent) {
        super(parent, true, false);

        contentAndLabelProvider = new ViewContentAndLabelProvider();
        viewer.setContentProvider(contentAndLabelProvider);
        viewer.setLabelProvider(contentAndLabelProvider);

        setComparator(new InstanceComparator(this, ViewContentAndLabelProvider.LAUNCH_TIME_COLUMN));

        refreshInstanceListTimer = new RefreshTimer(this);
        refreshInstanceListTimer.startTimer();
    }

    /**
     * Creates a new instance selection table within the specified composite,
     * displaying only the Amazon EC2 instance IDs specified.
     *
     * @param parent
     *            The parent composite for this new instance selection table.
     * @param instancesToDisplay
     *            A list of Amazon EC2 instance IDs to limit this selection
     *            table to showing.
     */
    public InstanceSelectionTable(Composite parent, List<String> instancesToDisplay) {
        this(parent);

        setInstancesToList(instancesToDisplay);
    }

    /**
     * Sets the period, in milliseconds, between automatic refreshes of the data
     * displayed in this instance selection table.
     *
     * @param refreshPeriodInMilliseconds
     *            The period, in milliseconds, between automatic refreshes of
     *            the data displayed in this table.
     */
    public void setRefreshPeriod(int refreshPeriodInMilliseconds) {
        refreshInstanceListTimer.setRefreshPeriod(refreshPeriodInMilliseconds);
    }

    /**
     * Refreshes the list of a user's current instances.
     */
    public void refreshInstances() {
        new RefreshInstancesThread().start();
    }

    /**
     * Returns the Action object that refreshes this selection table.
     *
     * @return The IAction object that refreshes this selection table.
     */
    public Action getRefreshAction() {
        return refreshAction;
    }

    /**
     * Returns the Action object that shows the Instance State filter dropdown menus
     *
     * @return The IAction object that shows the Instance State filter dropdown menus
     */
    public IAction getInstanceStateFilterDropDownAction() {
        return instanceStateFilterDropDownAction;
    }

    /**
     * Returns the Action object that shows the Security Group filter dropdown menus
     *
     * @return The Action object that shows the Security Group filter dropdown menus
     */
    public IAction getSecurityGroupFilterAction() {
        return securityGroupFilterDropDownAction;
    }

    /* (non-Javadoc)
     * @see org.eclipse.swt.widgets.Widget#dispose()
     */
    @Override
    public void dispose() {
        refreshInstanceListTimer.stopTimer();

        super.dispose();
    }

    /* (non-Javadoc)
     * @see com.amazonaws.eclipse.ec2.ui.IRefreshable#refreshData()
     */
    public void refreshData() {
        refreshInstances();
    }

    /**
     * Sets the Amazon EC2 instances which should be displayed in this selection
     * table. Specifying an empty list or a null list will both result in
     * displaying no instances in this list. The selection table will be
     * refreshed by this method so that the specified instances are being
     * displayed.
     *
     * @param serviceInstances
     *            A list of Amazon EC2 instance IDs to display in this selection
     *            table.
     */
    public void setInstancesToList(List<String> serviceInstances) {
        if (serviceInstances == null) {
            serviceInstances = new ArrayList<String>();
        }
        this.instancesToDisplay = serviceInstances;
        this.refreshInstances();
    }

    /*
     * SelectionTable Interface
     */

    /* (non-Javadoc)
     * @see com.amazonaws.eclipse.ec2.ui.SelectionTable#createColumns()
     */
    @Override
    protected void createColumns() {
        newColumn("Instance ID", 10);
        newColumn("Public DNS Name", 15);
        newColumn("Image ID", 10);
        newColumn("Root Device Type", 10);
        newColumn("State", 10);
        newColumn("Type", 10);
        newColumn("Availability Zone", 10);
        newColumn("Key Pair", 10);
        newColumn("Launch Time", 15);
        newColumn("Security Groups", 15);
        newColumn("Tags", 15);
    }

    /* (non-Javadoc)
     * @see com.amazonaws.eclipse.ec2.ui.SelectionTable#fillContextMenu(org.eclipse.jface.action.IMenuManager)
     */
    @Override
    protected void fillContextMenu(IMenuManager manager) {
        manager.add(refreshAction);
        manager.add(new Separator());
        manager.add(rebootAction);
        manager.add(terminateAction);
        manager.add(startInstancesAction);
        manager.add(stopInstancesAction);
        manager.add(new Separator());
        manager.add(openShellAction);
        manager.add(openShellDialogAction);
        manager.add(new Separator());
        manager.add(createAmiAction);
        manager.add(new Separator());
        manager.add(copyPublicDnsNameAction);

        final boolean isRunningInstanceSelected = isRunningInstanceSelected();
        final boolean exactlyOneInstanceSelected = isRunningInstanceSelected
                && (getAllSelectedInstances().size() == 1);

        if (exactlyOneInstanceSelected) {
            manager.add(new Separator());
            manager.add(createEbsMenu());
        }

        rebootAction.setEnabled(isRunningInstanceSelected);
        terminateAction.setEnabled(isRunningInstanceSelected);

        boolean instanceLaunchedWithKey = getSelectedInstance().getKeyName() != null;
        boolean knownPrivateKey = keyPairManager.isKeyPairValid(AwsToolkitCore.getDefault().getCurrentAccountId(),
                getSelectedInstance().getKeyName());
        boolean canOpenShell = isRunningInstanceSelected && instanceLaunchedWithKey && knownPrivateKey;
        openShellAction.setEnabled(canOpenShell);
        openShellDialogAction.setEnabled(canOpenShell);

        copyPublicDnsNameAction.setEnabled(exactlyOneInstanceSelected);
        createAmiAction.setEnabled(exactlyOneInstanceSelected && instanceLaunchedWithKey);

        // These calls seem like a no-op, but it basically forces a refresh of the enablement state
        startInstancesAction.setEnabled(startInstancesAction.isEnabled());
        stopInstancesAction.setEnabled(stopInstancesAction.isEnabled());
    }

    /* (non-Javadoc)
     * @see com.amazonaws.eclipse.ec2.ui.SelectionTable#makeActions()
     */
    @Override
    protected void makeActions() {
        refreshAction = new Action("Refresh", Ec2Plugin.getDefault().getImageRegistry().getDescriptor("refresh")) {
            public void run() {
                refreshInstances();
            }

            public String getToolTipText() {
                return "Refresh instances";
            }
        };

        rebootAction = new RebootInstancesAction(this);
        terminateAction = new TerminateInstancesAction(this);
        openShellAction = new OpenShellAction(this);
        openShellDialogAction = new OpenShellDialogAction(this);
        createAmiAction = new CreateAmiAction(this);

        copyPublicDnsNameAction = new Action("Copy Public DNS Name",
                Ec2Plugin.getDefault().getImageRegistry().getDescriptor("clipboard")) {
            public void run() {
                copyPublicDnsNameToClipboard(getSelectedInstance());
            }

            public String getToolTipText() {
                return "Copies this instance's public DNS name to the clipboard.";
            }
        };

        attachNewVolumeAction = new Action("Attach New Volume...") {
            public void run() {
                Instance instance = getSelectedInstance();

                CreateNewVolumeDialog dialog = new CreateNewVolumeDialog(Display.getCurrent().getActiveShell(),
                        instance);
                if (dialog.open() != IDialogConstants.OK_ID)
                    return;

                new AttachNewVolumeThread(instance, dialog.getSize(), dialog.getSnapshotId(), dialog.getDevice())
                        .start();
            }

            public String getToolTipText() {
                return "Attaches a new Elastic Block Storage volume to this instance.";
            }
        };

        startInstancesAction = new StartInstancesAction(this);
        stopInstancesAction = new StopInstancesAction(this);

        instanceStateDropDownMenuHandler = new MenuHandler();
        instanceStateDropDownMenuHandler.addListener(this);
        instanceStateDropDownMenuHandler.add("ALL", "All Instances", true);
        for (InstanceType instanceType : InstanceType.values()) {
            instanceStateDropDownMenuHandler.add(instanceType.id, instanceType.name + " Instances");
        }
        instanceStateDropDownMenuHandler.add("windows", "Windows Instances");
        instanceStateFilterDropDownAction = new MenuAction("Status Filter", "Filter by instance state", "filter",
                instanceStateDropDownMenuHandler);

        securityGroupDropDownMenuHandler = new MenuHandler();
        securityGroupDropDownMenuHandler.addListener(this);
        allSecurityGroupFilterItem = securityGroupDropDownMenuHandler.add("ALL", "All Security Groups", true);
        securityGroupFilterDropDownAction = new DynamicMenuAction("Security Groups Filter",
                "Filter by security group", "filter", securityGroupDropDownMenuHandler);
    }

    /*
     * Private Interface
     */

    protected void copyPublicDnsNameToClipboard(Instance instance) {
        Clipboard clipboard = new Clipboard(Display.getCurrent());

        String textData = instance.getPublicDnsName();
        TextTransfer textTransfer = TextTransfer.getInstance();
        Transfer[] transfers = new Transfer[] { textTransfer };
        Object[] data = new Object[] { textData };
        clipboard.setContents(data, transfers);
        clipboard.dispose();
    }

    /**
     * Returns true if and only if at least one instance is selected and all of
     * the selected instances are in a running state.
     *
     * @return True if and only if at least one instance is selected and all of
     *         the selected instances are in a running state, otherwise returns
     *         false.
     */
    private boolean isRunningInstanceSelected() {
        boolean instanceSelected = viewer.getTree().getSelectionCount() > 0;

        if (!instanceSelected) {
            return false;
        }

        for (TreeItem data : viewer.getTree().getSelection()) {
            Instance instance = (Instance) data.getData();

            if (!instance.getState().getName().equalsIgnoreCase("running")) {
                return false;
            }
        }

        return true;
    }

    @SuppressWarnings("unchecked")
    List<Instance> getAllSelectedInstances() {
        StructuredSelection selection = (StructuredSelection) viewer.getSelection();
        return (List<Instance>) selection.toList();
    }

    private Instance getSelectedInstance() {
        StructuredSelection selection = (StructuredSelection) viewer.getSelection();
        return (Instance) selection.getFirstElement();
    }

    private IMenuManager createEbsMenu() {
        final MenuManager subMenu = new MenuManager("Elastic Block Storage");
        subMenu.add(attachNewVolumeAction);
        subMenu.add(new Separator());

        final Instance instance = getSelectedInstance();

        new PopulateEbsMenuThread(instance, subMenu).start();

        return subMenu;
    }

    /**
     * Sets the list of instances to be displayed in the instance table.
     *
     * @param instances
     *            The list of instances to be displayed in the instance table.
     * @param securityGroupMap
     *            A map of instance IDs to a list of security groups in which
     *            those instances were launched.
     */
    private void setInput(final List<Instance> instances, final Map<String, List<String>> securityGroupMap) {
        Display.getDefault().asyncExec(new Runnable() {
            @SuppressWarnings("unchecked")
            public void run() {
                /*
                 * Sometimes we see cases where the content provider for a table
                 * viewer is null, so we check for that here to avoid causing an
                 * unhandled event loop exception. All InstanceSelectionTables
                 * should always have a content provider set in the constructor,
                 * but for some reason we occasionally still see this happen.
                 */
                if (viewer.getContentProvider() == null) {
                    return;
                }

                StructuredSelection currentSelection = (StructuredSelection) viewer.getSelection();
                viewer.setInput(new InstancesViewInput(instances, securityGroupMap));

                packColumns();

                Set<String> instanceIds = new HashSet<String>();
                for (Instance instance : (List<Instance>) currentSelection.toList()) {
                    instanceIds.add(instance.getInstanceId());
                }

                List<Instance> newSelectedInstances = new ArrayList<Instance>();
                for (TreeItem treeItem : viewer.getTree().getItems()) {
                    Instance instance = (Instance) treeItem.getData();

                    if (instanceIds.contains(instance.getInstanceId())) {
                        newSelectedInstances.add(instance);
                    }
                }

                viewer.setSelection(new StructuredSelection(newSelectedInstances));
            }
        });
    }

    /*
     * Private Classes
     */

    /**
     * Thread for making a service call to EC2 to list current instances.
     */
    private class RefreshInstancesThread extends Thread {
        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        @Override
        public void run() {
            if (selectionTableListener != null) {
                selectionTableListener.loadingData();
                enableDropDowns(false);
            }

            List<Reservation> reservations = null;
            try {
                boolean needsToDescribeInstances = true;
                DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest();
                if (instancesToDisplay != null) {
                    /*
                     * If the caller explicitly asked for a list of zero
                     * instances to be displayed, don't even bother querying for
                     * anything.
                     */
                    if (instancesToDisplay.size() == 0) {
                        needsToDescribeInstances = false;
                    }
                    ;

                    describeInstancesRequest.setInstanceIds(instancesToDisplay);
                }

                final List<Instance> allInstances = new ArrayList<Instance>();
                final Map<String, List<String>> securityGroupsByInstanceId = new HashMap<String, List<String>>();

                if (needsToDescribeInstances) {
                    DescribeInstancesResult response = getAwsEc2Client()
                            .describeInstances(describeInstancesRequest);
                    reservations = response.getReservations();

                    noOfInstances = -1; //Reset the value

                    Set<String> allSecurityGroups = new TreeSet<String>();

                    for (Reservation reservation : reservations) {
                        List<Instance> instances = reservation.getInstances();

                        List<String> groupNames = reservation.getGroupNames();
                        Collections.sort(groupNames);
                        allSecurityGroups.addAll(groupNames);

                        //Filter Security Groups
                        if (securityGroupDropDownMenuHandler.getCurrentSelection().getMenuId().equals("ALL")
                                || groupNames.contains(
                                        securityGroupDropDownMenuHandler.getCurrentSelection().getMenuId())) {

                            for (Instance instance : instances) {
                                //Filter Instances
                                if (!instanceStateDropDownMenuHandler.getCurrentSelection().getMenuId()
                                        .equals("ALL")) {
                                    if (instanceStateDropDownMenuHandler.getCurrentSelection().getMenuId()
                                            .equalsIgnoreCase("windows")) {
                                        if (instance.getPlatform() == null
                                                || !instance.getPlatform().equals("windows"))
                                            continue;
                                    } else if (!instance.getInstanceType().equalsIgnoreCase(
                                            instanceStateDropDownMenuHandler.getCurrentSelection().getMenuId())) {
                                        continue;
                                    }
                                }

                                allInstances.add(instance);

                                // Populate the map of instance IDs -> security groups
                                securityGroupsByInstanceId.put(instance.getInstanceId(), groupNames);
                            }
                        }
                    }

                    //Populate all Security Groups dynamically
                    securityGroupDropDownMenuHandler.clear();
                    securityGroupDropDownMenuHandler.add(allSecurityGroupFilterItem);
                    for (String securityGroup : allSecurityGroups) {
                        securityGroupDropDownMenuHandler.add(new MenuItem(securityGroup, securityGroup));
                    }
                }

                noOfInstances = allInstances.size();
                setInput(allInstances, securityGroupsByInstanceId);
            } catch (Exception e) {
                // Only log an error if the account info is valid and we
                // actually expected this call to work
                if (AwsToolkitCore.getDefault().getAccountInfo().isValid()) {
                    Status status = new Status(IStatus.ERROR, Ec2Plugin.PLUGIN_ID,
                            "Unable to list instances: " + e.getMessage(), e);
                    StatusManager.getManager().handle(status, StatusManager.LOG);
                }
            } finally {
                if (selectionTableListener != null) {
                    selectionTableListener.finishedLoadingData(noOfInstances);
                    enableDropDowns(true);
                }
            }
        }
    }

    /**
     * Callback function. Is called from the DropdownMenuHandler when a menu option is clicked
     *
     *  @param itemSelected The selected MenuItem
     *
     *  @see com.amazonaws.eclipse.ec2.utils.IMenu#menuClicked(com.amazonaws.eclipse.ec2.utils.IMenu.MenuItem)
     */
    public void menuClicked(MenuItem menuItemSelected) {
        refreshData();
    }

    /**
     * Enables/Disables dropdown filters
     *
     * @param checked A TRUE value will imply the dropdown filter is enabled; FALSE value will make it disabled
     */
    private void enableDropDowns(boolean checked) {
        instanceStateFilterDropDownAction.setEnabled(checked);
        securityGroupFilterDropDownAction.setEnabled(checked);
    }
}