You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/03/20 15:23:02 UTC

[48/51] [abbrv] [partial] incubator-taverna-workbench git commit: taverna-workbench-* -> taverna-*

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java
new file mode 100644
index 0000000..0b01fe2
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java
@@ -0,0 +1,411 @@
+/*******************************************************************************
+ * Copyright (C) 2007-2009 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.servicedescriptions.ServiceDescription.LOCAL_SERVICES;
+import static net.sf.taverna.t2.servicedescriptions.ServiceDescription.SERVICE_TEMPLATES;
+
+import java.awt.BorderLayout;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.events.AbstractProviderEvent;
+import net.sf.taverna.t2.servicedescriptions.events.AbstractProviderNotification;
+import net.sf.taverna.t2.servicedescriptions.events.PartialServiceDescriptionsNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification;
+import net.sf.taverna.t2.servicedescriptions.events.RemovedProviderEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeModel;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+/**
+ * A panel of available services
+ *
+ * @author Stian Soiland-Reyes
+ */
+@SuppressWarnings("serial")
+public class ServicePanel extends JPanel implements UIComponentSPI {
+	private static Logger logger = Logger.getLogger(ServicePanel.class);
+	private static final int INITIAL_BLANK_OUT_COUNTER = 2;
+	public static final String AVAILABLE_SERVICES = "Available services";
+	public static final String MATCHING_SERVIES = "Matching services";
+	public static final String NO_MATCHING_SERVICES = "No matching services";
+	public static final String MOBY_OBJECTS = "MOBY Objects";
+	/**
+	 * A Comparable constant to be used with buildPathMap
+	 */
+	private static final String SERVICES = "4DA84170-7746-4817-8C2E-E29AF8B2984D";
+	private static final int STATUS_LINE_MESSAGE_MS = 600;
+	private static ServicePathElementComparator servicePathElementComparator = new ServicePathElementComparator();
+
+	public int blankOutCounter = 0;
+	private TreeUpdaterThread updaterThread;
+	private RootFilterTreeNode root = new RootFilterTreeNode(AVAILABLE_SERVICES);
+	private ServiceTreePanel serviceTreePanel;
+	private JLabel statusLine;
+	private FilterTreeModel treeModel;
+	protected Timer statusUpdateTimer;
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	protected final ServiceDescriptionRegistryObserver serviceDescriptionRegistryObserver = new ServiceDescriptionRegistryObserver();
+	protected final Object updateLock = new Object();
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServicePanel(ServiceDescriptionRegistry serviceDescriptionRegistry,
+			EditManager editManager, MenuManager menuManager,
+			SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+		serviceDescriptionRegistry.addObserver(serviceDescriptionRegistryObserver);
+		initialise();
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Service panel";
+	}
+
+	@Override
+	public void onDisplay() {
+	}
+
+	@Override
+	public void onDispose() {
+	}
+
+	public void providerStatus(ServiceDescriptionProvider provider, String message) {
+		logger.info(message + " " + provider);
+		final String htmlMessage = "<small>" + message + " [" + provider + "]</small>";
+
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				blankOutCounter = INITIAL_BLANK_OUT_COUNTER;
+				statusLine.setText("<html>" + htmlMessage + "</html>");
+				statusLine.setVisible(true);
+			}
+		});
+	}
+
+	protected void initialise() {
+		removeAll();
+		setLayout(new BorderLayout());
+		treeModel = new FilterTreeModel(root);
+		serviceTreePanel = new ServiceTreePanel(treeModel, serviceDescriptionRegistry, editManager, menuManager, selectionManager, serviceRegistry);
+		serviceTreePanel.setAvailableObjectsString(AVAILABLE_SERVICES);
+		serviceTreePanel.setMatchingObjectsString(MATCHING_SERVIES);
+		serviceTreePanel.setNoMatchingObjectsString(NO_MATCHING_SERVICES);
+		add(serviceTreePanel);
+		statusLine = new JLabel();
+		add(statusLine, BorderLayout.SOUTH);
+		if (statusUpdateTimer != null)
+			statusUpdateTimer.cancel();
+		statusUpdateTimer = new Timer("Clear status line", true);
+		statusUpdateTimer
+				.scheduleAtFixedRate(new UpdateStatusLineTask(), 0, STATUS_LINE_MESSAGE_MS);
+		updateTree();
+	}
+
+	protected void updateTree() {
+		synchronized (updateLock) {
+			if (updaterThread != null && updaterThread.isAlive()) {
+				return;
+			}
+			updaterThread = new TreeUpdaterThread();
+			updaterThread.start();
+		}
+	}
+
+	protected static class ServicePathElementComparator implements Comparator<Object> {
+		@Override
+		public int compare(Object o1, Object o2) {
+			if ((o1 instanceof String) && (o2 instanceof String)) {
+				if (o1.equals(SERVICE_TEMPLATES))
+					return -1;
+				else if (o2.equals(SERVICE_TEMPLATES))
+					return 1;
+				if (o1.equals(LOCAL_SERVICES))
+					return -1;
+				else if (o2.equals(LOCAL_SERVICES))
+					return 1;
+				if (o1.equals(MOBY_OBJECTS))
+					return -1;
+				else if (o2.equals(MOBY_OBJECTS))
+					return 1;
+			}
+			return o1.toString().compareToIgnoreCase(o2.toString());
+		}
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	//FIXME this class is type-disastrous! Really bad.
+	public class TreeUpdaterThread extends Thread {
+		private boolean aborting = false;
+
+		private TreeUpdaterThread() {
+			super("Updating service panel");
+			setDaemon(true);
+		}
+
+		public void abort() {
+			aborting = true;
+			interrupt();
+		}
+
+		@Override
+		public void run() {
+			Map<Comparable, Map> pathMap = buildPathMap();
+			populateChildren(root, pathMap);
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					try {
+						serviceTreePanel.runFilter();
+					} catch (InterruptedException | InvocationTargetException e) {
+						logger.error("failed to filter", e);
+					}
+				}
+			});
+		}
+
+		protected Map<Comparable, Map> buildPathMap() {
+			Map<Comparable, Map> paths = new TreeMap<>();
+			for (ServiceDescription serviceDescription : serviceDescriptionRegistry
+					.getServiceDescriptions()) {
+				if (aborting)
+					return paths;
+				Map currentPath = paths;
+				for (Object pathElem : serviceDescription.getPath()) {
+					Map pathEntry = (Map) currentPath.get(pathElem);
+					if (pathEntry == null) {
+						pathEntry = new TreeMap();
+						currentPath.put(pathElem, pathEntry);
+					}
+					currentPath = pathEntry;
+				}
+				TreeMap<String, Set<ServiceDescription>> services = (TreeMap) currentPath
+						.get(SERVICES);
+				if (services == null) {
+					services = new TreeMap<>();
+					currentPath.put(SERVICES, services);
+				}
+				String serviceDescriptionName = serviceDescription.getName();
+				if (!services.containsKey(serviceDescriptionName)) {
+					Set<ServiceDescription> serviceSet = new HashSet<>();
+					services.put(serviceDescriptionName, serviceSet);
+				}
+				services.get(serviceDescriptionName).add(serviceDescription);
+			}
+			return paths;
+		}
+
+		protected void populateChildren(FilterTreeNode node, Map pathMap) {
+			if (aborting)
+				return;
+			if (node == root) {
+				// Clear top root
+				invokeLater(new Runnable() {
+					@Override
+					public void run() {
+						if (aborting)
+							return;
+						serviceTreePanel.setFilter(null);
+						root.removeAllChildren();
+					}
+				});
+			}
+
+			Set<Comparable> paths = new TreeSet<>(servicePathElementComparator);
+			Map<String, Set<ServiceDescription>> services = (Map) pathMap
+					.get(SERVICES);
+			if (services == null)
+				services = new TreeMap<>();
+			paths.addAll(pathMap.keySet());
+			paths.addAll(services.keySet());
+
+			for (Comparable pathElement : paths) {
+				if (aborting)
+					return;
+				if (pathElement.equals(SERVICES))
+					continue;
+				Set<FilterTreeNode> childNodes = new HashSet<>();
+				if (services.containsKey(pathElement)) {
+					for (ServiceDescription sd : services.get(pathElement))
+						childNodes.add(new ServiceFilterTreeNode(sd));
+				} else
+					childNodes.add(new PathElementFilterTreeNode((String) pathElement));
+				invokeLater(new AddNodeRunnable(node, childNodes));
+				if ((pathMap.containsKey(pathElement)) && !childNodes.isEmpty())
+					populateChildren(childNodes.iterator().next(), (Map) pathMap.get(pathElement));
+			}
+			// if (!services.isEmpty()) {
+			// Collections.sort(services, serviceComparator);
+			// for (String serviceName : services.keySet()) {
+			// if (aborting) {
+			// return;
+			// }
+			// if (pathMap.containsKey(serviceName)) {
+			// continue;
+			// }
+			// SwingUtilities.invokeLater(new AddNodeRunnable(node,
+			// new ServiceFilterTreeNode(services.get(serviceName))));
+			// }
+			// }
+		}
+
+		public class AddNodeRunnable implements Runnable {
+			private final Set<FilterTreeNode> nodes;
+			private final FilterTreeNode root;
+
+			public AddNodeRunnable(FilterTreeNode root, Set<FilterTreeNode> nodes) {
+				this.root = root;
+				this.nodes = nodes;
+			}
+
+			@Override
+			public void run() {
+				if (aborting)
+					return;
+				for (FilterTreeNode n : nodes)
+					root.add(n);
+			}
+		}
+	}
+
+	public static class RemoveNodeRunnable implements Runnable {
+		private final FilterTreeNode root;
+
+		public RemoveNodeRunnable(FilterTreeNode root) {
+			this.root = root;
+		}
+
+		@Override
+		public void run() {
+			root.removeFromParent();
+		}
+	}
+
+	private final class ServiceDescriptionRegistryObserver implements
+			Observer<ServiceDescriptionRegistryEvent> {
+		Set<ServiceDescriptionProvider> alreadyComplainedAbout = new HashSet<>();
+
+		@Override
+		public void notify(Observable<ServiceDescriptionRegistryEvent> sender,
+				ServiceDescriptionRegistryEvent message) throws Exception {
+			if (message instanceof ProviderErrorNotification)
+				reportServiceProviderError((ProviderErrorNotification) message);
+			else if (message instanceof ServiceDescriptionProvidedEvent
+					|| message instanceof RemovedProviderEvent) {
+				AbstractProviderEvent ape = (AbstractProviderEvent) message;
+				alreadyComplainedAbout.remove(ape.getProvider());
+			}
+
+			if (message instanceof AbstractProviderNotification) {
+				AbstractProviderNotification abstractProviderNotification = (AbstractProviderNotification) message;
+				providerStatus(abstractProviderNotification.getProvider(),
+						abstractProviderNotification.getMessage());
+			}
+			if (message instanceof PartialServiceDescriptionsNotification)
+				/*
+				 * TODO: Support other events and only update relevant parts of
+				 * tree, or at least select the recently added provider
+				 */
+				updateTree();
+			else if (message instanceof RemovedProviderEvent)
+				updateTree();
+		}
+
+		private void reportServiceProviderError(
+				final ProviderErrorNotification pen) {
+			ServiceDescriptionProvider provider = pen.getProvider();
+			if (serviceDescriptionRegistry
+					.getDefaultServiceDescriptionProviders().contains(provider))
+				return;
+			if (alreadyComplainedAbout.contains(provider))
+				return;
+
+			alreadyComplainedAbout.add(provider);
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					showMessageDialog(ServicePanel.this, pen.getMessage()
+							+ "\n" + pen.getProvider(), "Import service error",
+							ERROR_MESSAGE);
+				}
+			});
+		}
+	}
+
+	private final class UpdateStatusLineTask extends TimerTask {
+		@Override
+		public void run() {
+			if (blankOutCounter < 0 || blankOutCounter-- > 0)
+				// Only clear it once
+				return;
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					if (blankOutCounter < 0)
+						statusLine.setVisible(false);
+				}
+			});
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java
new file mode 100644
index 0000000..2eb97ef
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (C) 2009 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel;
+
+import javax.swing.ImageIcon;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+/**
+ * Service panel factory
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class ServicePanelComponentFactory implements UIComponentFactorySPI {
+	private ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private EditManager editManager;
+	private MenuManager menuManager;
+	private SelectionManager selectionManager;
+	private ServiceRegistry serviceRegistry;
+
+	@Override
+	public UIComponentSPI getComponent() {
+		return new ServicePanel(serviceDescriptionRegistry, editManager,
+				menuManager, selectionManager, serviceRegistry);
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Service panel";
+	}
+
+	public void setServiceDescriptionRegistry(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setServiceRegistry(ServiceRegistry serviceRegistry) {
+		this.serviceRegistry = serviceRegistry;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
new file mode 100644
index 0000000..4b7388d
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel;
+
+import static net.sf.taverna.t2.workbench.activityicons.DefaultActivityIcon.getDefaultIcon;
+
+import java.awt.Component;
+
+import javax.swing.Icon;
+import javax.swing.JTree;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeCellRenderer;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+@SuppressWarnings("serial")
+public class ServiceTreeCellRenderer extends FilterTreeCellRenderer {
+	@Override
+	public Component getTreeCellRendererComponent(JTree tree, Object value,
+			boolean sel, boolean expanded, boolean leaf, int row,
+			boolean hasFocus) {
+		Component result = super.getTreeCellRendererComponent(tree, value, sel,
+				expanded, leaf, row, hasFocus);
+		if (result instanceof ServiceTreeCellRenderer
+				&& value instanceof FilterTreeNode
+				&& ((FilterTreeNode) value).getUserObject() instanceof ServiceDescription)
+			prettifyServiceTreeCell((ServiceTreeCellRenderer) result,
+					(ServiceDescription) ((FilterTreeNode) value)
+							.getUserObject());
+		else {
+			// Commented out - these are ugly, use the default folder icons instead
+			/*
+			 * if (expanded) { ((ServiceTreeCellRenderer) result)
+			 * .setIcon(WorkbenchIcons.folderOpenIcon); } else {
+			 * ((ServiceTreeCellRenderer) result)
+			 * .setIcon(WorkbenchIcons.folderClosedIcon); }
+			 */
+		}
+		return result;
+	}
+
+	private void prettifyServiceTreeCell(ServiceTreeCellRenderer renderer,
+			ServiceDescription item) {
+		String name = item.getName();
+		if (getFilter() != null)
+			name = getFilter().filterRepresentation(name);
+		// serviceTreeCellRenderer.setForeground(Color.red);
+		String displayName = name;
+
+		String textualDescription = item.getDescription();
+		if (textualDescription != null && !textualDescription.isEmpty())
+			displayName = displayName + " - " + textualDescription;
+		renderer.setText(displayName);
+
+		Icon activityIcon = item.getIcon();
+		renderer.setIcon(activityIcon != null ? activityIcon
+				: getDefaultIcon());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java
new file mode 100644
index 0000000..a76dcc9
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java
@@ -0,0 +1,252 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel;
+
+import static java.awt.Color.RED;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.BLUE;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.ORANGE;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.halfShade;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.minusIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.plusIcon;
+import static net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView.importServiceDescription;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.swing.AbstractAction;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ExportServiceDescriptionsAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromFileAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromURLAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RemoveDefaultServicesAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RemoveUserServicesAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RestoreDefaultServicesAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeSelectionModel;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.TreePanel;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+/**
+ * @author alanrw
+ */
+public class ServiceTreeClickListener extends MouseAdapter {
+	private static Logger logger = Logger.getLogger(ServiceTreeClickListener.class);
+
+	private JTree tree;
+	private TreePanel panel;
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServiceTreeClickListener(JTree tree, TreePanel panel,
+			ServiceDescriptionRegistry serviceDescriptionRegistry, EditManager editManager,
+			MenuManager menuManager, SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		this.tree = tree;
+		this.panel = panel;
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+	}
+
+	@SuppressWarnings("serial")
+	private void handleMouseEvent(MouseEvent evt) {
+		FilterTreeSelectionModel selectionModel = (FilterTreeSelectionModel) tree
+				.getSelectionModel();
+		// Discover the tree row that was clicked on
+		int selRow = tree.getRowForLocation(evt.getX(), evt.getY());
+		if (selRow == -1)
+			return;
+
+		// Get the selection path for the row
+		TreePath selectionPath = tree
+				.getPathForLocation(evt.getX(), evt.getY());
+		if (selectionPath == null)
+			return;
+
+		// Get the selected node
+		final FilterTreeNode selectedNode = (FilterTreeNode) selectionPath
+				.getLastPathComponent();
+
+		selectionModel.clearSelection();
+		selectionModel.mySetSelectionPath(selectionPath);
+
+		if (evt.isPopupTrigger()) {
+			JPopupMenu menu = new JPopupMenu();
+			Object selectedObject = selectedNode.getUserObject();
+			logger.info(selectedObject.getClass().getName());
+			if (!(selectedObject instanceof ServiceDescription)) {
+				menu.add(new ShadedLabel("Tree", BLUE));
+				menu.add(new JMenuItem(new AbstractAction("Expand all",
+						plusIcon) {
+					@Override
+					public void actionPerformed(ActionEvent evt) {
+						invokeLater(new Runnable() {
+							@Override
+							public void run() {
+								panel.expandAll(selectedNode, true);
+							}
+						});
+					}
+				}));
+				menu.add(new JMenuItem(new AbstractAction("Collapse all",
+						minusIcon) {
+					@Override
+					public void actionPerformed(ActionEvent evt) {
+						invokeLater(new Runnable() {
+							@Override
+							public void run() {
+								panel.expandAll(selectedNode, false);
+							}
+						});
+					}
+				}));
+			}
+
+			if (selectedObject instanceof ServiceDescription) {
+				final ServiceDescription sd = (ServiceDescription) selectedObject;
+				menu.add(new ShadedLabel(sd.getName(), ORANGE));
+				menu.add(new AbstractAction("Add to workflow") {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						importServiceDescription(sd, false, editManager,
+								menuManager, selectionManager, serviceRegistry);
+					}
+				});
+				menu.add(new AbstractAction("Add to workflow with name...") {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						importServiceDescription(sd, true, editManager,
+								menuManager, selectionManager, serviceRegistry);
+					}
+				});
+			}
+
+			Map<String, ServiceDescriptionProvider> nameMap = getServiceDescriptionProviderMap(selectedNode);
+
+			boolean first = true;
+			for (String name : nameMap.keySet()) {
+				final ServiceDescriptionProvider sdp = nameMap.get(name);
+				if (!(sdp instanceof ConfigurableServiceProvider))
+					continue;
+				if (first) {
+					menu.add(new ShadedLabel(
+							"Remove individual service provider", GREEN));
+					first = false;
+				}
+				menu.add(new AbstractAction(name) {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						serviceDescriptionRegistry
+								.removeServiceDescriptionProvider(sdp);
+					}
+				});
+			}
+
+			if (selectedNode.isRoot()) { // Root "Available services"
+				menu.add(new ShadedLabel("Default and added service providers",
+						ORANGE));
+				menu.add(new RemoveUserServicesAction(
+						serviceDescriptionRegistry));
+				menu.add(new RemoveDefaultServicesAction(
+						serviceDescriptionRegistry));
+				menu.add(new RestoreDefaultServicesAction(
+						serviceDescriptionRegistry));
+
+				menu.add(new ShadedLabel("Import/export services", halfShade(RED)));
+				menu.add(new ImportServiceDescriptionsFromFileAction(
+						serviceDescriptionRegistry));
+				menu.add(new ImportServiceDescriptionsFromURLAction(
+						serviceDescriptionRegistry));
+				menu.add(new ExportServiceDescriptionsAction(
+						serviceDescriptionRegistry));
+			}
+
+			menu.show(evt.getComponent(), evt.getX(), evt.getY());
+		}
+	}
+
+	private Map<String, ServiceDescriptionProvider> getServiceDescriptionProviderMap(
+			FilterTreeNode selectedNode) {
+		Set<ServiceDescriptionProvider> providers;
+
+		if (selectedNode.isRoot())
+			providers = serviceDescriptionRegistry
+					.getServiceDescriptionProviders();
+		else {
+			providers = new HashSet<>();
+			for (FilterTreeNode leaf : selectedNode.getLeaves()) {
+				if (!leaf.isLeaf())
+					logger.info("Not a leaf");
+				if (!(leaf.getUserObject() instanceof ServiceDescription)) {
+					logger.info(leaf.getUserObject().getClass()
+							.getCanonicalName());
+					logger.info(leaf.getUserObject().toString());
+					continue;
+				}
+				providers
+						.addAll(serviceDescriptionRegistry
+								.getServiceDescriptionProviders((ServiceDescription) leaf
+										.getUserObject()));
+			}
+		}
+
+		TreeMap<String, ServiceDescriptionProvider> nameMap = new TreeMap<>();
+		for (ServiceDescriptionProvider sdp : providers)
+			nameMap.put(sdp.toString(), sdp);
+		return nameMap;
+	}
+
+	@Override
+	public void mousePressed(MouseEvent evt) {
+		handleMouseEvent(evt);
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent evt) {
+		handleMouseEvent(evt);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java
new file mode 100644
index 0000000..ce3e336
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java
@@ -0,0 +1,176 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.ui.servicepanel;
+
+import static java.awt.datatransfer.DataFlavor.javaJVMLocalObjectMimeType;
+import static javax.swing.SwingUtilities.invokeLater;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.TransferHandler;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeWillExpandListener;
+import javax.swing.tree.ExpandVetoException;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.servicepanel.menu.AddServiceProviderMenu;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.Filter;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeModel;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.TreePanel;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+public class ServiceTreePanel extends TreePanel {
+	private static final long serialVersionUID = 6611462684296693909L;
+	private static Logger logger = Logger.getLogger(ServiceTreePanel.class);
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServiceTreePanel(FilterTreeModel treeModel,
+			ServiceDescriptionRegistry serviceDescriptionRegistry, EditManager editManager,
+			MenuManager menuManager, SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		super(treeModel);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+		initialize();
+	}
+
+	@Override
+	protected void initialize() {
+		super.initialize();
+		tree.setDragEnabled(true);
+		tree.setTransferHandler(new ServiceTransferHandler());
+		tree.addTreeWillExpandListener(new AvoidRootCollapse());
+		tree.expandRow(0);
+
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				tree.addMouseListener(new ServiceTreeClickListener(tree,
+						ServiceTreePanel.this, serviceDescriptionRegistry,
+						editManager, menuManager, selectionManager,
+						serviceRegistry));
+			}
+		});
+	}
+
+	@Override
+	protected Component createExtraComponent() {
+		JComponent buttonPanel = new JPanel(new FlowLayout());
+		buttonPanel.add(new AddServiceProviderMenu(serviceDescriptionRegistry));
+		// buttonPanel.add(new JButton(new RefreshProviderRegistryAction()));
+		return buttonPanel;
+	}
+
+	@Override
+	public Filter createFilter(String text) {
+		return new ServiceFilter(text, filterTreeModel.getRoot());
+	}
+
+	@Override
+	protected TreeCellRenderer createCellRenderer() {
+		return new ServiceTreeCellRenderer();
+	}
+
+	public static class AvoidRootCollapse implements TreeWillExpandListener {
+		@Override
+		public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
+			if (event.getPath().getPathCount() == 1)
+				throw new ExpandVetoException(event, "Can't collapse root");
+		}
+
+		@Override
+		public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
+		}
+	}
+
+	private final class ServiceTransferHandler extends TransferHandler {
+		private static final long serialVersionUID = 4347965626386951176L;
+
+		/**
+		 * Triggered when a node ie. an {@link ActivityItem} is dragged out of
+		 * the tree. Figures out what node it is being dragged and then starts a
+		 * drag action with it
+		 */
+		@Override
+		protected Transferable createTransferable(JComponent c) {
+			TreePath selectionPath = tree.getSelectionPath();
+			if (selectionPath == null)
+				return null;
+			FilterTreeNode lastPathComponent = (FilterTreeNode) selectionPath
+					.getLastPathComponent();
+			if (!(lastPathComponent.getUserObject() instanceof ServiceDescription))
+				return null;
+			final ServiceDescription serviceDescription = (ServiceDescription) lastPathComponent
+					.getUserObject();
+
+			return new Transferable() {
+				@Override
+				public Object getTransferData(DataFlavor flavor)
+						throws UnsupportedFlavorException, IOException {
+					return serviceDescription;
+				}
+
+				@Override
+				public DataFlavor[] getTransferDataFlavors() {
+					DataFlavor[] flavors = new DataFlavor[1];
+					try {
+						flavors[0] = getFlavorForClass(ServiceDescription.class);
+					} catch (ClassNotFoundException e) {
+						logger.error("Error casting Dataflavor", e);
+						flavors[0] = null;
+					}
+					return flavors;
+				}
+
+				@Override
+				public boolean isDataFlavorSupported(DataFlavor flavor) {
+					DataFlavor thisFlavor = null;
+					try {
+						thisFlavor = getFlavorForClass(ServiceDescription.class);
+					} catch (ClassNotFoundException e) {
+						logger.error("Error casting Dataflavor", e);
+					}
+					return flavor.equals(thisFlavor);
+				}
+			};
+		}
+
+		@Override
+		public int getSourceActions(JComponent c) {
+			return COPY_OR_MOVE;
+		}
+	}
+
+	private DataFlavor getFlavorForClass(Class<?> clazz)
+			throws ClassNotFoundException {
+		String name = clazz.getName();
+		return new DataFlavor(javaJVMLocalObjectMimeType + ";class=" + clazz,
+				name.substring(name.lastIndexOf('.') + 1),
+				clazz.getClassLoader());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java
new file mode 100644
index 0000000..c8f2bfa
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.BorderLayout.WEST;
+import static java.awt.event.KeyEvent.VK_ENTER;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow;
+import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors;
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.uibuilder.UIBuilder;
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.CustomizedConfigurePanelProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.CustomizedConfigurePanelProvider.CustomizedConfigureCallBack;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+/**
+ * Action for adding a service provider
+ * 
+ * @author Stian Soiland-Reyes
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class AddServiceProviderAction extends AbstractAction {
+	private static Logger logger = getLogger(AddServiceProviderAction.class);
+
+	// protected static Dimension DIALOG_SIZE = new Dimension(400, 300);
+
+	private ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	private final ConfigurableServiceProvider confProvider;
+	private final Component owner;
+
+	public AddServiceProviderAction(ConfigurableServiceProvider confProvider,
+			Component owner) {
+		super(confProvider.getName() + "...", confProvider.getIcon());
+		this.confProvider = confProvider;
+		this.owner = owner;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		if (confProvider instanceof CustomizedConfigurePanelProvider) {
+			final CustomizedConfigurePanelProvider provider = (CustomizedConfigurePanelProvider) confProvider;
+			provider.createCustomizedConfigurePanel(new CustomizedConfigureCallBack() {
+				@Override
+				public Configuration getTemplateConfig() {
+					return (Configuration) provider.getConfiguration().clone();
+				}
+
+				@Override
+				public ServiceDescriptionRegistry getServiceDescriptionRegistry() {
+					return AddServiceProviderAction.this.getServiceDescriptionRegistry();
+				}
+
+				@Override
+				public void newProviderConfiguration(Configuration providerConfig) {
+					addNewProvider(providerConfig);
+				}
+			});
+			return;
+		}
+
+		Configuration configuration;
+		try {
+			configuration = (Configuration) confProvider.getConfiguration().clone();
+		} catch (Exception ex) {
+			throw new RuntimeException("Can't clone configuration bean", ex);
+		}
+		JPanel buildEditor = buildEditor(configuration);
+		String title = "Add " + confProvider.getName();
+		JDialog dialog = new HelpEnabledDialog(getMainWindow(), title, true, null);
+		JPanel iconPanel = new JPanel();
+		iconPanel.add(new JLabel(confProvider.getIcon()), NORTH);
+		dialog.add(iconPanel, WEST);
+		dialog.add(buildEditor, CENTER);
+		JPanel buttonPanel = new JPanel(new BorderLayout());
+		final AddProviderAction addProviderAction = new AddProviderAction(configuration,
+				dialog);
+		JButton addProviderButton = new JButton(addProviderAction);
+		buttonPanel.add(addProviderButton, WEST);
+		
+		dialog.add(buttonPanel, SOUTH);
+	    // When user presses "Return" key fire the action on the "Add" button
+		addProviderButton.addKeyListener(new KeyAdapter() {
+			@Override
+			public void keyPressed(KeyEvent evt) {
+				if (evt.getKeyCode() == VK_ENTER)
+					addProviderAction.actionPerformed(null);
+			}
+		});
+		dialog.getRootPane().setDefaultButton(addProviderButton);
+		
+		// dialog.setSize(buttonPanel.getPreferredSize());
+		dialog.pack();
+		dialog.setLocationRelativeTo(owner);
+//		dialog.setLocation(owner.getLocationOnScreen().x + owner.getWidth(),
+//				owner.getLocationOnScreen().y + owner.getHeight());
+		dialog.setVisible(true);
+	}
+
+	protected void addNewProvider(Configuration configurationBean) {
+		ConfigurableServiceProvider cloned = (ConfigurableServiceProvider) confProvider
+				.newInstance();
+		try {
+			cloned.configure(configurationBean);
+			getServiceDescriptionRegistry().addObserver(
+					new CheckAddedCorrectlyObserver(cloned));
+			getServiceDescriptionRegistry().addServiceDescriptionProvider(
+					cloned);
+		} catch (Exception ex) {
+			logger.warn("Can't configure provider " + cloned + " using "
+					+ configurationBean, ex);
+			showMessageDialog(owner, "Can't configure service provider "
+					+ cloned.getName(), "Can't add service provider",
+					ERROR_MESSAGE);
+		}
+	}
+
+	private PropertyDescriptor[] getProperties(Configuration configuration) {
+		// FIXME This is *so* wrong!
+		try {
+			return getPropertyDescriptors(configuration);
+		} catch (Exception ex) {
+			throw new RuntimeException("Can't inspect configuration bean", ex);
+		}
+	}
+
+	// TODO This is probably not right
+	protected JPanel buildEditor(Configuration configuration) {
+		List<String> uiBuilderConfig = new ArrayList<>();
+		int lastPreferred = 0;
+		for (PropertyDescriptor property : getProperties(configuration)) {
+			if (property.isHidden() || property.isExpert())
+				// TODO: Add support for expert properties
+				continue;
+			String propertySpec = property.getName() + ":name="
+					+ property.getDisplayName();
+			if (property.isPreferred())
+				// Add it to the front
+				uiBuilderConfig.add(lastPreferred++, propertySpec);
+			else
+				uiBuilderConfig.add(propertySpec);
+		}
+
+		return UIBuilder.buildEditor(configuration, uiBuilderConfig
+				.toArray(new String[0]));
+	}
+
+	public void setServiceDescriptionRegistry(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public ServiceDescriptionRegistry getServiceDescriptionRegistry() {
+		return serviceDescriptionRegistry;
+	}
+
+	public class AddProviderAction extends AbstractAction {
+		private final Configuration configurationBean;
+		private final JDialog dialog;
+
+		private AddProviderAction(Configuration configurationBean, JDialog dialog) {
+			super("Add");
+			this.configurationBean = configurationBean;
+			this.dialog = dialog;
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			addNewProvider(configurationBean);
+			dialog.setVisible(false);
+		}
+	}
+
+	public class CheckAddedCorrectlyObserver implements
+			Observer<ServiceDescriptionRegistryEvent> {
+		private final ConfigurableServiceProvider provider;
+
+		private CheckAddedCorrectlyObserver(ConfigurableServiceProvider provider) {
+			this.provider = provider;
+		}
+
+		@Override
+		public void notify(Observable<ServiceDescriptionRegistryEvent> sender,
+				ServiceDescriptionRegistryEvent message) throws Exception {
+			if (message instanceof ProviderErrorNotification)
+				notify((ProviderErrorNotification) message);
+			else if (message instanceof ServiceDescriptionProvidedEvent)
+				notify((ServiceDescriptionProvidedEvent) message);
+		}
+
+		private void notify(ServiceDescriptionProvidedEvent providedMsg) {
+			if (providedMsg.getProvider() == provider)
+				getServiceDescriptionRegistry().removeObserver(this);
+		}
+
+		private void notify(ProviderErrorNotification errorMsg) {
+			if (errorMsg.getProvider() != provider)
+				return;
+			getServiceDescriptionRegistry().removeObserver(this);
+			getServiceDescriptionRegistry().removeServiceDescriptionProvider(
+					provider);
+//			showMessageDialog(owner, errorMsg.getMessage(),
+//					"Can't add provider " + provider, ERROR_MESSAGE);
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java
new file mode 100644
index 0000000..990e429
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Action to export the current service descritpions from the Service
+ * Registry to an xml file.
+ *
+ * @author Alex Nenadic
+ */
+//FIXME this file assumes we're writing out as XML
+@SuppressWarnings("serial")
+public class ExportServiceDescriptionsAction extends AbstractAction {
+	private static final String EXTENSION = ".xml";
+	private static final String EXPORT_SERVICES = "Export services to file";
+	private static final String SERVICE_EXPORT_DIR_PROPERTY = "serviceExportDir";
+	private Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class);
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public ExportServiceDescriptionsAction(ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(EXPORT_SERVICES);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public static final boolean INHIBIT = true;
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		JComponent parentComponent = null;
+		if (e.getSource() instanceof JComponent)
+			parentComponent = (JComponent) e.getSource();
+
+		if (INHIBIT) {
+			showMessageDialog(parentComponent,
+					"Operation not currently working correctly",
+					"Not Implemented", ERROR_MESSAGE);
+			return;
+		}
+
+		JFileChooser fileChooser = new JFileChooser();
+		Preferences prefs = Preferences.userNodeForPackage(getClass());
+		String curDir = prefs.get(SERVICE_EXPORT_DIR_PROPERTY,
+				System.getProperty("user.home"));
+		fileChooser.setDialogTitle("Select file to export services to");
+
+		fileChooser.setFileFilter(new FileFilter() {
+			@Override
+			public boolean accept(File f) {
+				return f.isDirectory()
+						|| f.getName().toLowerCase().endsWith(EXTENSION);
+			}
+
+			@Override
+			public String getDescription() {
+				return ".xml files";
+			}
+		});
+
+		fileChooser.setCurrentDirectory(new File(curDir));
+
+		boolean tryAgain = true;
+		while (tryAgain) {
+			tryAgain = false;
+			int returnVal = fileChooser.showSaveDialog(parentComponent);
+			if (returnVal == APPROVE_OPTION) {
+				prefs.put(SERVICE_EXPORT_DIR_PROPERTY, fileChooser.getCurrentDirectory()
+						.toString());
+				File file = fileChooser.getSelectedFile();
+				if (!file.getName().toLowerCase().endsWith(EXTENSION)) {
+					String newName = file.getName() + EXTENSION;
+					file = new File(file.getParentFile(), newName);
+				}
+
+				try {
+					if (file.exists()) {
+						String msg = "Are you sure you want to overwrite existing file "
+								+ file + "?";
+						int ret = showConfirmDialog(parentComponent, msg,
+								"File already exists", YES_NO_CANCEL_OPTION);
+						if (ret == NO_OPTION) {
+							tryAgain = true;
+							continue;
+						} else if (ret != YES_OPTION) {
+							logger.info("Service descriptions export: aborted overwrite of "
+									+ file.getAbsolutePath());
+							break;
+						}
+					}
+					exportServiceDescriptions(file);
+					break;
+				} catch (Exception ex) {
+					logger.error("Service descriptions export: failed to export services to "
+							+ file.getAbsolutePath(), ex);
+					showMessageDialog(
+							parentComponent,
+							"Failed to export services to "
+									+ file.getAbsolutePath(), "Error",
+							ERROR_MESSAGE);
+					break;
+				}
+			}
+		}
+
+		if (parentComponent instanceof JButton)
+			// lose the focus from the button after performing the action
+			parentComponent.requestFocusInWindow();
+	}
+
+	private void exportServiceDescriptions(File file) {
+		// TODO: Open in separate thread to avoid hanging UI
+		serviceDescriptionRegistry.exportCurrentServiceDescriptions(file);
+		logger.info("Service descriptions export: saved to file "
+				+ file.getAbsolutePath());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java
new file mode 100644
index 0000000..1542583
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.HashSet;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+/**
+ * Action to import a list of service descriptions from an xml file
+ * into the Service Registry. Users have an option to completely
+ * replace the current services or just add the ones from the file
+ * to the current services.
+ *
+ * @author Alex Nenadic
+ */
+//FIXME this file assumes we're writing out as XML
+@SuppressWarnings("serial")
+public class ImportServiceDescriptionsFromFileAction extends AbstractAction{
+	private static final String EXTENSION = ".xml";
+	private static final String IMPORT_SERVICES = "Import services from file";
+	private static final String SERVICE_IMPORT_DIR_PROPERTY = "serviceImportDir";
+	private static final Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class);
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public ImportServiceDescriptionsFromFileAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(IMPORT_SERVICES);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	private static final Object[] CHOICES = { "Add to current services",
+			"Replace current services", "Cancel" };
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		JComponent parentComponent = null;
+		if (e.getSource() instanceof JComponent)
+			parentComponent = (JComponent) e.getSource();
+
+		if (ExportServiceDescriptionsAction.INHIBIT) {
+			showMessageDialog(parentComponent,
+					"Operation not currently working correctly",
+					"Not Implemented", ERROR_MESSAGE);
+			return;
+		}
+
+		int choice = showOptionDialog(
+				parentComponent,
+				"Do you want to add the imported services to the current ones or replace the current ones?",
+				"Import services", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE,
+				null, CHOICES, CHOICES[0]);
+
+		if (choice != CANCEL_OPTION) {
+			JFileChooser fileChooser = new JFileChooser();
+			Preferences prefs = Preferences.userNodeForPackage(getClass());
+			String curDir = prefs.get(SERVICE_IMPORT_DIR_PROPERTY, System.getProperty("user.home"));
+
+			fileChooser.setDialogTitle("Select file to import services from");
+
+			fileChooser.setFileFilter(new FileFilter() {
+				@Override
+				public boolean accept(File f) {
+					return f.isDirectory()
+							|| f.getName().toLowerCase().endsWith(EXTENSION);
+				}
+
+				@Override
+				public String getDescription() {
+					return EXTENSION + " files";
+				}
+			});
+
+			fileChooser.setCurrentDirectory(new File(curDir));
+
+			if (fileChooser.showOpenDialog(parentComponent) == APPROVE_OPTION) {
+				prefs.put(SERVICE_IMPORT_DIR_PROPERTY, fileChooser
+						.getCurrentDirectory().toString());
+				File file = fileChooser.getSelectedFile();
+
+				try {
+					// Did user want to replace or add services?
+					importServices(file, choice == YES_OPTION);
+				} catch (Exception ex) {
+					logger.error(
+							"Service descriptions import: failed to import services from "
+									+ file.getAbsolutePath(), ex);
+					showMessageDialog(parentComponent,
+							"Failed to import services from " + file.getAbsolutePath(), "Error",
+							ERROR_MESSAGE);
+				}
+			}
+		}
+
+		if (parentComponent instanceof JButton)
+			// lose the focus from the button after performing the action
+			parentComponent.requestFocusInWindow();
+	}
+
+	private void importServices(final File file, final boolean addToCurrent)
+			throws Exception {
+		// TODO: Open in separate thread to avoid hanging UI
+
+		if (!addToCurrent)
+			for (ServiceDescriptionProvider provider : new HashSet<>(
+					serviceDescriptionRegistry.getServiceDescriptionProviders()))
+				// remove all configurable service providers
+				if (provider instanceof ConfigurableServiceProvider)
+					serviceDescriptionRegistry
+							.removeServiceDescriptionProvider(provider);
+
+		// import all providers from the file
+		serviceDescriptionRegistry.loadServiceProviders(file);
+		serviceDescriptionRegistry.saveServiceDescriptions();
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java
new file mode 100644
index 0000000..0dbbe25
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showInputDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+
+import java.awt.event.ActionEvent;
+import java.net.URL;
+import java.util.HashSet;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Action to import a list of service descriptions from an URL pointing
+ * to an xml file into the Service Registry. Users have an option to
+ * completely replace the current services or just add the ones from the
+ * file to the current services.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class ImportServiceDescriptionsFromURLAction extends AbstractAction{
+	private static final String IMPORT_SERVICES_FROM_URL = "Import services from URL";
+	private static final Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class);
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public ImportServiceDescriptionsFromURLAction(ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(IMPORT_SERVICES_FROM_URL);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	private static final Object[] CHOICES = { "Add to current services",
+			"Replace current services", "Cancel" };
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		JComponent parentComponent = null;
+		if (e.getSource() instanceof JComponent)
+			parentComponent = (JComponent) e.getSource();
+
+		if (ExportServiceDescriptionsAction.INHIBIT) {
+			showMessageDialog(parentComponent,
+					"Operation not currently working correctly",
+					"Not Implemented", ERROR_MESSAGE);
+			return;
+		}
+
+		int choice = showOptionDialog(
+				parentComponent,
+				"Do you want to add the imported services to the current ones or replace the current ones?",
+				"Import services", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE,
+				null, CHOICES, CHOICES[0]);
+
+		if (choice != CANCEL_OPTION) {
+			final String urlString = (String) showInputDialog(parentComponent,
+					"Enter the URL of the service descriptions file to import",
+					"Service Descriptions URL", QUESTION_MESSAGE, null, null,
+					"http://");
+			try {
+				if (urlString != null && !urlString.isEmpty())
+					// Did user want to replace or add services?
+					importServices(urlString, choice == YES_OPTION);
+			} catch (Exception ex) {
+				logger.error(
+						"Service descriptions import: failed to import services from "
+								+ urlString, ex);
+				showMessageDialog(parentComponent,
+						"Failed to import services from " + urlString, "Error",
+						ERROR_MESSAGE);
+			}
+		}
+
+		if (parentComponent instanceof JButton)
+			// lose the focus from the button after performing the action
+			parentComponent.requestFocusInWindow();
+	}
+
+	private void importServices(final String urlString, final boolean addToCurrent)
+			throws Exception {
+		// TODO: Open in separate thread to avoid hanging UI
+		URL url = new URL(urlString);
+
+		if (!addToCurrent)
+			for (ServiceDescriptionProvider provider : new HashSet<>(
+					serviceDescriptionRegistry.getServiceDescriptionProviders()))
+				// remove all configurable service providers
+				if (provider instanceof ConfigurableServiceProvider)
+					serviceDescriptionRegistry
+							.removeServiceDescriptionProvider(provider);
+
+		// import all providers from the URL
+		serviceDescriptionRegistry.loadServiceProviders(url);
+		serviceDescriptionRegistry.saveServiceDescriptions();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java
new file mode 100644
index 0000000..9c4c84b
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+/**
+ * Action for refreshing the service provider registry.
+ * <p>
+ * This would typically re-parse WSDLs, etc.
+ * 
+ * @see ServiceDescriptionRegistry#refresh()
+ * @author Stian Soiland-Reyes
+ */
+@SuppressWarnings("serial")
+public class RefreshProviderRegistryAction extends AbstractAction {
+	private static final String REFRESH = "Reload services";
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RefreshProviderRegistryAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(REFRESH);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		serviceDescriptionRegistry.refresh();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java
new file mode 100644
index 0000000..b6ba606
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+@SuppressWarnings("serial")
+public class RemoveDefaultServicesAction extends AbstractAction {
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RemoveDefaultServicesAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super("Remove default service providers");
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		for (ServiceDescriptionProvider provider : serviceDescriptionRegistry
+				.getDefaultServiceDescriptionProviders()) {
+			if (!(provider instanceof ConfigurableServiceProvider))
+				continue;
+			serviceDescriptionRegistry
+					.removeServiceDescriptionProvider(provider);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java
new file mode 100644
index 0000000..bf0a771
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JLabel;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+@SuppressWarnings("serial")
+public class RemoveUserServicesAction extends AbstractAction {
+	private static final String CONFIRM_MESSAGE = "You are about to remove all services you have added. <br>"
+			+ "Are you <b>really</b> sure you want to do this?";
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RemoveUserServicesAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super("Remove all user added service providers");
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		int option = showConfirmDialog(null, new JLabel("<html><body>"
+				+ CONFIRM_MESSAGE + "</body></html>"),
+				"Confirm service deletion", YES_NO_OPTION);
+
+		if (option == YES_OPTION)
+			for (ServiceDescriptionProvider provider : serviceDescriptionRegistry
+					.getUserAddedServiceProviders())
+				serviceDescriptionRegistry
+						.removeServiceDescriptionProvider(provider);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java
new file mode 100644
index 0000000..c7071ed
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+@SuppressWarnings("serial")
+public class RestoreDefaultServicesAction extends AbstractAction {
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RestoreDefaultServicesAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super("Restore default service providers");
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		for (ServiceDescriptionProvider provider : serviceDescriptionRegistry
+				.getDefaultServiceDescriptionProviders()) {
+			if (!(provider instanceof ConfigurableServiceProvider))
+				continue;
+			serviceDescriptionRegistry.addServiceDescriptionProvider(provider);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java
new file mode 100644
index 0000000..f666877
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.servicepanel.config;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration;
+import net.sf.taverna.t2.workbench.helper.Helper;
+
+@SuppressWarnings("serial")
+public class ServiceDescriptionConfigPanel extends JPanel {
+	private static final String REMOVE_PERMANENTLY = "Allow permanent removal of default service providers";
+	private static final String INCLUDE_DEFAULTS = "Include default service providers";
+
+	private final ServiceDescriptionsConfiguration config;
+	private JCheckBox includeDefaults;
+	private JCheckBox removePermanently;
+	private final ServiceDescriptionRegistry serviceDescRegistry;
+
+	public ServiceDescriptionConfigPanel(ServiceDescriptionsConfiguration config,
+			ServiceDescriptionRegistry serviceDescRegistry) {
+		super(new GridBagLayout());
+		this.config = config;
+		this.serviceDescRegistry = serviceDescRegistry;
+		initialize();
+	}
+
+	private void initialize() {
+		removeAll();
+
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		// Title describing what kind of settings we are configuring here
+		JTextArea descriptionText = new JTextArea(
+				"Configure behaviour of default service providers in Service Panel");
+        descriptionText.setLineWrap(true);
+        descriptionText.setWrapStyleWord(true);
+        descriptionText.setEditable(false);
+        descriptionText.setFocusable(false);
+        descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10));
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.anchor = WEST;
+		gbc.fill = HORIZONTAL;
+		add(descriptionText, gbc);
+
+		includeDefaults = new JCheckBox(INCLUDE_DEFAULTS);
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.anchor = WEST;
+		gbc.fill = NONE;
+        gbc.insets = new Insets(10, 0, 0, 0);
+		add(includeDefaults, gbc);
+
+		removePermanently = new JCheckBox(REMOVE_PERMANENTLY);
+		gbc.gridx = 0;
+		gbc.gridy = 2;
+        gbc.insets = new Insets(0, 0, 0, 0);
+		add(removePermanently, gbc);
+
+		// Filler
+		gbc.gridx = 0;
+		gbc.gridy = 3;
+		gbc.weighty = 1;
+		gbc.weightx = 1;
+		gbc.fill = GridBagConstraints.BOTH;
+        gbc.insets = new Insets(10, 0, 0, 0);
+		add(createButtonPanel(), gbc);
+
+		setFields(config);
+	}
+
+	/**
+	 * Create the panel to contain the buttons
+	 *
+	 * @return
+	 */
+	private JPanel createButtonPanel() {
+		final JPanel panel = new JPanel();
+
+		/**
+		 * The helpButton shows help about the current component
+		 */
+		JButton helpButton = new JButton(new AbstractAction("Help") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				Helper.showHelp(panel);
+			}
+		});
+		panel.add(helpButton);
+
+		/**
+		 * The resetButton changes the property values shown to those
+		 * corresponding to the configuration currently applied.
+		 */
+		JButton resetButton = new JButton(new AbstractAction("Reset") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				setFields(config);
+			}
+		});
+		panel.add(resetButton);
+
+		/**
+		 * The applyButton applies the shown field values to the
+		 * {@link HttpProxyConfiguration} and saves them for future.
+		 */
+		JButton applyButton = new JButton(new AbstractAction("Apply") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				applySettings();
+				setFields(config);
+			}
+		});
+		panel.add(applyButton);
+
+		return panel;
+	}
+
+	protected void applySettings() {
+		// Include default service providers
+		config.setIncludeDefaults(includeDefaults.isSelected());
+		for (ServiceDescriptionProvider provider : serviceDescRegistry
+				.getDefaultServiceDescriptionProviders()) {
+			if (! (provider instanceof ConfigurableServiceProvider))
+				continue;
+			if (config.isIncludeDefaults())
+				serviceDescRegistry.addServiceDescriptionProvider(provider);
+			else
+				serviceDescRegistry.removeServiceDescriptionProvider(provider);
+		}
+
+		// Allow permanent removal of default service providers
+		config.setRemovePermanently(removePermanently.isSelected());
+	}
+
+	/**
+	 * Set the shown configuration field values to those currently in use
+	 * (i.e. last saved configuration).
+	 *
+	 */
+	private void setFields(ServiceDescriptionsConfiguration configurable) {
+		includeDefaults.setSelected(configurable.isIncludeDefaults());
+		removePermanently.setSelected(configurable.isRemovePermanently());
+	}
+}