/*
* Copyright (C) 2011 Ives van der Flaas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package be.ac.ua.comp.scarletnebula.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingWorker;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dasein.cloud.network.Firewall;
import org.dasein.cloud.network.FirewallRule;
import org.dasein.cloud.network.Protocol;
import be.ac.ua.comp.scarletnebula.core.CloudProvider;
import be.ac.ua.comp.scarletnebula.gui.windows.AddFirewallRuleWindow;
import be.ac.ua.comp.scarletnebula.gui.windows.AddFirewallRuleWindow.AddFirewallRuleWindowClosedListener;
import be.ac.ua.comp.scarletnebula.misc.SwingWorkerWithThrobber;
import be.ac.ua.comp.scarletnebula.misc.Utils;
public class InteractiveFirewallPanel extends JPanel implements
AddFirewallRuleWindowClosedListener {
private final class AddRuleSwingWorker extends
SwingWorkerWithThrobber<Exception, Object> {
private final String cidr;
private final int beginPort;
private final Protocol protocol;
private final int endPort;
private final Firewall firewall;
private AddRuleSwingWorker(final Collapsable throbber,
final String cidr, final int beginPort,
final Protocol protocol, final int endPort,
final Firewall firewall) {
super(throbber);
this.cidr = cidr;
this.beginPort = beginPort;
this.protocol = protocol;
this.endPort = endPort;
this.firewall = firewall;
}
@Override
protected Exception doInBackground() throws Exception {
try {
provider.addFirewallRule(firewall, beginPort, endPort,
protocol, cidr);
} catch (final Exception e) {
return e;
}
return null;
}
@Override
public void done() {
try {
final Exception result = get();
if (result == null) {
// Succeeded
ruleList.addRule(new FirewallRule(firewall
.getProviderFirewallId(), cidr, protocol,
beginPort, endPort));
} else {
log.error("Exception was thrown while creating new Rule",
result);
JOptionPane.showMessageDialog(
InteractiveFirewallPanel.this,
result.getLocalizedMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
}
} catch (final Exception ignore) {
}
}
}
public static class PortRange {
final public int startPort;
final public int endPort;
public PortRange(final String range) {
final String portString = range;
final String portParts[] = portString.split("-");
final Collection<Integer> ports = new ArrayList<Integer>(
portParts.length);
for (final String portPart : portParts) {
ports.add(Integer.decode(portPart));
}
startPort = Utils.min(ports);
endPort = Utils.max(ports);
}
}
private final class DeleteRuleActionListener implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
for (final Firewall firewall : getSelectedFirewalls()) {
(new SwingWorker<Exception, Object>() {
@Override
protected Exception doInBackground() throws Exception {
try {
final int selectedRow = ruleList.getSelectedRow();
final PortRange range = new PortRange(
ruleList.getPort(selectedRow));
final Protocol protocol = ruleList
.getProtocol(selectedRow);
final String ip = ruleList.getSource(selectedRow);
ruleList.datamodel.removeRow(selectedRow);
provider.deleteFirewallRule(firewall,
range.startPort, range.endPort, protocol,
ip);
return null;
} catch (Exception e) {
return e;
}
}
@Override
public void done() {
try {
final Exception result = get();
if (result != null) {
log.error("Error while removing rule", result);
}
} catch (final Exception ignore) {
}
}
}).execute();
}
}
}
private final class AddRuleActionListener implements ActionListener {
private final CloudProvider provider;
private AddRuleActionListener(final CloudProvider provider) {
this.provider = provider;
}
@Override
public void actionPerformed(final ActionEvent e) {
final Collection<Firewall> firewalls = getSelectedFirewalls();
if (firewalls.size() != 1) {
return;
}
final AddFirewallRuleWindow win = new AddFirewallRuleWindow(
(JDialog) Utils.findWindow(InteractiveFirewallPanel.this),
provider, firewalls.iterator().next());
win.addAddFirewallRuleWindowClosed(InteractiveFirewallPanel.this);
win.setVisible(true);
}
}
private final class AddFirewallActionListener implements ActionListener {
private final CloudProvider provider;
private AddFirewallActionListener(final CloudProvider provider) {
this.provider = provider;
}
@Override
public void actionPerformed(final ActionEvent e) {
boolean invalid = true;
String name = "";
while (invalid) {
name = JOptionPane.showInputDialog(
InteractiveFirewallPanel.this,
"Enter a name for the new Firewall:", "New firewall",
JOptionPane.QUESTION_MESSAGE);
if (name == null) {
return;
} else {
if (Pattern.matches("[a-zA-Z0-9 ]+", name)) {
invalid = false;
} else {
JOptionPane
.showMessageDialog(
InteractiveFirewallPanel.this,
"A firewall name can only contain letters, numbers and spaces.",
"Incorrect Firewallname",
JOptionPane.ERROR_MESSAGE);
}
}
}
final String firewallName = name;
(new NewFirewallSwingWorkerWithThrobber(
creatingFirewallThrobberPanel, firewallName, provider))
.execute();
}
}
private final class DeleteFirewallActionListener implements ActionListener {
private final CloudProvider provider;
private DeleteFirewallActionListener(final CloudProvider provider) {
this.provider = provider;
}
@Override
public void actionPerformed(final ActionEvent e) {
final String selectedFirewall = (String) firewallList
.getSelectedValue();
final int result = JOptionPane.showOptionDialog(
InteractiveFirewallPanel.this,
"Are you sure you want to remove the firewall named "
+ selectedFirewall + "?", "Remove firewall?",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
null, new Object[] { "Delete Firewall", "Cancel" },
"Cancel");
if (result != JOptionPane.OK_OPTION) {
return;
}
firewallListModel.removeElement(selectedFirewall);
(new DeleteFirewallSwingWorker(selectedFirewall, provider))
.execute();
}
}
private final class LoadFirewallsWorkerWithThrobber extends
SwingWorkerWithThrobber<Exception, Firewall> {
private LoadFirewallsWorkerWithThrobber(final Collapsable throbber) {
super(throbber);
}
@Override
protected Exception doInBackground() throws Exception {
try {
for (final Firewall firewall : provider.getFirewalls()) {
publish(firewall);
}
} catch (final Exception e) {
return e;
}
return null;
}
@Override
public void process(final List<Firewall> input) {
for (final Firewall firewall : input) {
addFirewall(firewall);
}
}
@Override
public void done() {
try {
final Exception result = get();
if (result != null) {
log.error("Something went wrong while querying firewalls",
result);
JOptionPane.showInternalMessageDialog(
InteractiveFirewallPanel.this,
result.getLocalizedMessage());
}
} catch (final Exception ignore) {
}
}
}
private final class NewFirewallSwingWorkerWithThrobber extends
SwingWorkerWithThrobber<Exception, Firewall> {
private final String firewallName;
private final CloudProvider provider;
private NewFirewallSwingWorkerWithThrobber(final Collapsable throbber,
final String firewallName, final CloudProvider provider) {
super(throbber);
this.firewallName = firewallName;
this.provider = provider;
}
@Override
protected Exception doInBackground() throws Exception {
try {
final Firewall fw = provider.createFirewall(firewallName);
publish(fw);
} catch (final Exception e) {
return e;
}
return null;
}
@Override
public void process(final List<Firewall> theFirewall) {
// There will only be 1 firewall in theFirewall...
for (final Firewall fw : theFirewall) {
addFirewall(fw);
addRule(fw, 22, 22, Protocol.TCP, "0.0.0.0/0");
}
firewallList.setSelectedIndex(firewallListModel.getSize() - 1);
}
}
private final class DeleteFirewallSwingWorker extends
SwingWorker<Object, Object> {
private final String selectedFirewall;
private final CloudProvider provider;
private DeleteFirewallSwingWorker(final String selectedFirewall,
final CloudProvider provider) {
this.selectedFirewall = selectedFirewall;
this.provider = provider;
}
@Override
protected Exception doInBackground() throws Exception {
try {
for (final Firewall firewall : firewalls) {
if (firewall.getName().equals(selectedFirewall)) {
provider.deleteFirewall(firewall);
break;
}
}
} catch (final Exception e) {
return e;
}
return null;
}
}
public class FirewallList extends JList {
private static final long serialVersionUID = 1L;
public FirewallList(final ListModel firewallListModel) {
super(firewallListModel);
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
}
}
private final class FirewallListSelectionListener implements
ListSelectionListener {
private final RuleList ruleList;
private final JButton deleteRuleButton;
private final JButton deleteFirewallButton;
public FirewallListSelectionListener(final RuleList ruleList,
final JButton addRuleButton, final JButton deleteRuleButton,
final JButton deleteFirewallButton) {
this.ruleList = ruleList;
this.deleteRuleButton = deleteRuleButton;
this.deleteFirewallButton = deleteFirewallButton;
}
@Override
public void valueChanged(final ListSelectionEvent e) {
final ListSelectionModel lsm = (ListSelectionModel) e.getSource();
final int minIndex = lsm.getMinSelectionIndex();
if (e.getValueIsAdjusting()) {
return;
}
if (minIndex == -1) {
ruleList.clear();
ruleList.setEnabled(false);
addRulebutton.setEnabled(false);
deleteRuleButton.setEnabled(false);
deleteFirewallButton.setEnabled(false);
} else {
ruleList.setEnabled(true);
addRulebutton.setEnabled(true);
deleteRuleButton.setEnabled(true);
deleteFirewallButton.setEnabled(true);
final String firewallName = (String) firewallListModel
.getElementAt(minIndex);
for (final Firewall firewall : firewalls) {
if (firewall.getName().equals(firewallName)) {
ruleList.displayFirewall(firewall);
break;
}
}
}
}
}
public class RuleList extends JTable {
private final class LoadRulesSwingWorkerWithThrobber extends
SwingWorkerWithThrobber<Collection<FirewallRule>, Object> {
private final Firewall firewall;
private LoadRulesSwingWorkerWithThrobber(
final Collapsable throbber, final Firewall firewall) {
super(throbber);
this.firewall = firewall;
}
@Override
protected Collection<FirewallRule> doInBackground()
throws Exception {
final Collection<FirewallRule> rules = provider
.getFirewallRules(firewall.getProviderFirewallId());
return rules;
}
@Override
public void done() {
try {
final Collection<FirewallRule> rules = get();
for (final FirewallRule rule : rules) {
addRule(rule);
}
} catch (final Exception ignore) {
}
}
}
private static final long serialVersionUID = 1L;
private final DefaultTableModel datamodel = new DefaultTableModel();
private final CloudProvider provider;
public RuleList(final CloudProvider provider) {
this.provider = provider;
setCellSelectionEnabled(false);
setRowSelectionAllowed(true);
datamodel.addColumn("Port");
datamodel.addColumn("Protocol");
datamodel.addColumn("Source");
setModel(datamodel);
getColumn("Port").setMaxWidth(100);
getColumn("Protocol").setMaxWidth(100);
}
public String getPort(final int row) {
return (String) getValueAt(row, 0);
}
public Protocol getProtocol(final int row) {
return (Protocol) getValueAt(row, 1);
}
public String getSource(final int row) {
return (String) getValueAt(row, 2);
}
public void addRule(final FirewallRule rule) {
final String portString = (rule.getStartPort() == rule.getEndPort()) ? Integer
.toString(rule.getStartPort()) : rule.getStartPort() + "-"
+ rule.getEndPort();
datamodel.addRow(new Object[] { portString, rule.getProtocol(),
rule.getCidr() });
}
public void clear() {
datamodel.setNumRows(0);
}
public void displayFirewall(final Firewall firewall) {
synchronized (this) {
clear();
(new LoadRulesSwingWorkerWithThrobber(
loadingRulesThrobberPanel, firewall)).execute();
}
}
}
private static final long serialVersionUID = 1L;
private final DefaultListModel firewallListModel = new DefaultListModel();
private final FirewallList firewallList = new FirewallList(
firewallListModel);
private final CollapsablePanel loadingFirewallsThrobberPanel = ThrobberFactory
.getCollapsableThrobber("Loading firewalls", 10, 10);
private final CollapsablePanel loadingRulesThrobberPanel = ThrobberFactory
.getCollapsableThrobber("Loading rules", 10, 10);
private final CollapsablePanel creatingFirewallThrobberPanel = ThrobberFactory
.getCollapsableThrobber("Creating firewall", 10, 10);
private final CollapsablePanel creatingRuleThrobberPanel = ThrobberFactory
.getCollapsableThrobber("Creating rule", 10, 10);
private final CloudProvider provider;
private static Log log = LogFactory.getLog(InteractiveFirewallPanel.class);
private final Collection<Firewall> firewalls = new ArrayList<Firewall>();
private final JButton addRulebutton = new JButton("Add Rule",
Utils.icon("add16.png"));
private final JButton deleteRuleButton = new JButton("Delete Rule",
Utils.icon("remove16.png"));
private final JButton addFirewallButton = new JButton("Add Firewall",
Utils.icon("add16.png"));
private final JButton deleteFirewallButton = new JButton("Delete Firewall",
Utils.icon("remove16.png"));
private final RuleList ruleList;
public InteractiveFirewallPanel(final CloudProvider provider) {
super(new BorderLayout());
this.provider = provider;
ruleList = new RuleList(provider);
final JPanel mainPanel = new JPanel(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.weightx = 0.25;
c.weighty = 1.0;
c.fill = GridBagConstraints.BOTH;
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(10, 10, 10, 5);
final JPanel firewallPanel = new JPanel(new BorderLayout());
firewallPanel.add(firewallList, BorderLayout.CENTER);
final JPanel firewallButtonPanel = new JPanel();
firewallButtonPanel.setLayout(new BoxLayout(firewallButtonPanel,
BoxLayout.PAGE_AXIS));
firewallButtonPanel.setOpaque(true);
firewallButtonPanel.setBackground(Color.white);
addFirewallButton.setMaximumSize(new Dimension(500, 500));
addFirewallButton.addActionListener(new AddFirewallActionListener(
provider));
firewallButtonPanel.add(addFirewallButton);
deleteFirewallButton.setMaximumSize(new Dimension(500, 500));
deleteFirewallButton
.addActionListener(new DeleteFirewallActionListener(provider));
firewallButtonPanel.add(deleteFirewallButton);
firewallPanel.add(firewallButtonPanel, BorderLayout.PAGE_END);
final JScrollPane firewallListScroller = new JScrollPane(firewallPanel);
firewallPanel.setPreferredSize(new Dimension(10, 10));
mainPanel.add(firewallListScroller, c);
c.weightx = 0.75;
c.gridx = 1;
c.insets = new Insets(10, 5, 10, 10);
ruleList.setFillsViewportHeight(true);
ruleList.setEnabled(false);
firewallList.getSelectionModel().addListSelectionListener(
new FirewallListSelectionListener(ruleList, addRulebutton,
deleteRuleButton, deleteFirewallButton));
final JScrollPane ruleListScroller = new JScrollPane(ruleList);
final JPanel ruleListPanel = new JPanel(new BorderLayout());
ruleListPanel.add(ruleListScroller, BorderLayout.CENTER);
final JPanel ruleListButtonPanel = new JPanel();
addRulebutton.addActionListener(new AddRuleActionListener(provider));
deleteRuleButton.addActionListener(new DeleteRuleActionListener());
ruleListButtonPanel.add(addRulebutton);
ruleListButtonPanel.add(deleteRuleButton);
addRulebutton.setEnabled(false);
deleteRuleButton.setEnabled(false);
deleteFirewallButton.setEnabled(false);
ruleListPanel.add(ruleListButtonPanel, BorderLayout.SOUTH);
ruleListPanel.setPreferredSize(new Dimension(10, 10));
mainPanel.add(ruleListPanel, c);
add(mainPanel, BorderLayout.CENTER);
final JPanel allThrobbersPanel = getThrobbersPanel();
add(allThrobbersPanel, BorderLayout.NORTH);
loadInitialFirewalls();
}
private JPanel getThrobbersPanel() {
final JPanel allThrobbersPanel = new JPanel(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.weightx = 1.0;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
allThrobbersPanel.add(loadingFirewallsThrobberPanel, c);
c.gridy = 1;
allThrobbersPanel.add(loadingRulesThrobberPanel, c);
c.gridy = 2;
allThrobbersPanel.add(creatingFirewallThrobberPanel, c);
c.gridy = 3;
allThrobbersPanel.add(creatingRuleThrobberPanel, c);
return allThrobbersPanel;
}
public Collection<Firewall> getSelectedFirewalls() {
final Collection<Firewall> rv = new ArrayList<Firewall>();
final int indices[] = firewallList.getSelectedIndices();
for (final int index : indices) {
final String firewall = (String) firewallListModel.get(index);
for (final Firewall comp : firewalls) {
if (comp.getName().equals(firewall)) {
rv.add(comp);
}
}
}
return rv;
}
public boolean selectedFirewallOpensPort(final int port) {
boolean found = false;
for (int row = 0; row < ruleList.getRowCount(); row++) {
final String portString = ruleList.getPort(row);
final PortRange range = new PortRange(portString);
if (port >= range.startPort && port <= range.endPort) {
found = true;
break;
}
}
return found;
}
private void addFirewall(final Firewall firewall) {
firewallListModel.addElement(firewall.getName());
firewalls.add(firewall);
}
private void loadInitialFirewalls() {
(new LoadFirewallsWorkerWithThrobber(loadingFirewallsThrobberPanel))
.execute();
}
@Override
public void addRuleWindowClosed(final Firewall firewall,
final int beginPort, final int endPort, final Protocol protocol,
final String cidr) {
addRule(firewall, beginPort, endPort, protocol, cidr);
}
private void addRule(final Firewall firewall, final int beginPort,
final int endPort, final Protocol protocol, final String cidr) {
(new AddRuleSwingWorker(creatingRuleThrobberPanel, cidr, beginPort,
protocol, endPort, firewall)).execute();
}
}