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/02/17 12:45:39 UTC

[15/52] [abbrv] incubator-taverna-workbench git commit: taverna-ui-impl/

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java
new file mode 100644
index 0000000..0a40bda
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java
@@ -0,0 +1,167 @@
+package net.sf.taverna.t2.servicedescriptions.impl;
+
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.CONFIGURATION;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.IGNORED;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDERS;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDER_ID;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.TYPE;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+class ServiceDescriptionDeserializer {
+	private List<ServiceDescriptionProvider> serviceDescriptionProviders;
+
+	ServiceDescriptionDeserializer(
+			List<ServiceDescriptionProvider> serviceDescriptionProviders) {
+		this.serviceDescriptionProviders = serviceDescriptionProviders;
+	}
+
+	public void deserialize(ServiceDescriptionRegistry registry,
+			File serviceDescriptionsFile) throws DeserializationException {
+		try (FileInputStream serviceDescriptionFileStream = new FileInputStream(
+				serviceDescriptionsFile)) {
+			deserialize(registry, serviceDescriptionFileStream);
+		} catch (FileNotFoundException ex) {
+			throw new DeserializationException("Could not locate file "
+					+ serviceDescriptionsFile.getAbsolutePath()
+					+ " containing service descriptions.");
+		} catch (IOException ex) {
+			throw new DeserializationException(
+					"Could not read stream containing service descriptions from "
+							+ serviceDescriptionsFile.getAbsolutePath(), ex);
+		}
+	}
+
+	public void deserialize(ServiceDescriptionRegistry registry,
+			URL serviceDescriptionsURL) throws DeserializationException {
+		try (InputStream serviceDescriptionInputStream = serviceDescriptionsURL
+				.openStream()) {
+			deserialize(registry, serviceDescriptionInputStream);
+		} catch (FileNotFoundException ex) {
+			throw new DeserializationException("Could not open URL "
+					+ serviceDescriptionsURL
+					+ " containing service descriptions.");
+		} catch (IOException ex) {
+			throw new DeserializationException(
+					"Could not read stream containing service descriptions from "
+							+ serviceDescriptionsURL, ex);
+		}
+	}
+
+	private static final JsonFactory factory = new JsonFactory();
+
+	private void deserialize(ServiceDescriptionRegistry registry,
+			InputStream serviceDescriptionsInputStream) throws IOException,
+			DeserializationException {
+		ObjectNode node = (ObjectNode) new ObjectMapper(factory)
+				.readTree(serviceDescriptionsInputStream);
+		List<ServiceDescriptionProvider> providers = deserializeProviders(node,
+				true);
+		for (ServiceDescriptionProvider provider : providers)
+			registry.addServiceDescriptionProvider(provider);
+	}
+
+	public Collection<? extends ServiceDescriptionProvider> deserializeDefaults(
+			ServiceDescriptionRegistry registry,
+			File defaultConfigurableServiceProvidersFile)
+			throws DeserializationException {
+		ObjectNode node;
+		try (FileInputStream serviceDescriptionStream = new FileInputStream(
+				defaultConfigurableServiceProvidersFile)) {
+			node = (ObjectNode) new ObjectMapper(factory)
+					.readTree(serviceDescriptionStream);
+		} catch (IOException e) {
+			throw new DeserializationException("Can't read "
+					+ defaultConfigurableServiceProvidersFile);
+		}
+		return deserializeProviders(node, false);
+	}
+
+	private List<ServiceDescriptionProvider> deserializeProviders(
+			ObjectNode rootNode, boolean obeyIgnored)
+			throws DeserializationException {
+		List<ServiceDescriptionProvider> providers = new ArrayList<>();
+
+		ArrayNode providersNode = (ArrayNode) rootNode.get(PROVIDERS);
+		if (providersNode != null)
+			for (JsonNode provider : providersNode)
+				providers.add(deserializeProvider((ObjectNode) provider));
+
+		if (obeyIgnored) {
+			ArrayNode ignoredNode = (ArrayNode) rootNode.get(IGNORED);
+			if (ignoredNode != null)
+				for (JsonNode provider : ignoredNode)
+					providers
+							.remove(deserializeProvider((ObjectNode) provider));
+		}
+
+		return providers;
+	}
+
+	private ServiceDescriptionProvider deserializeProvider(
+			ObjectNode providerNode) throws DeserializationException {
+		String providerId = providerNode.get(PROVIDER_ID).asText().trim();
+		ServiceDescriptionProvider provider = null;
+		for (ServiceDescriptionProvider serviceProvider : serviceDescriptionProviders)
+			if (serviceProvider.getId().equals(providerId)) {
+				provider = serviceProvider;
+				break;
+			}
+		if (provider == null)
+			throw new DeserializationException(
+					"Could not find provider with id " + providerId);
+
+		/*
+		 * So we know the service provider now, but we need a separate instance
+		 * of that provider for each providerElem. E.g. we can have 2 or more
+		 * WSDL provider elements and need to return a separate provider
+		 * instance for each as they will have different configurations.
+		 */
+		ServiceDescriptionProvider instance = provider.newInstance();
+
+		if (instance instanceof ConfigurableServiceProvider)
+			try {
+				Configuration config = new Configuration();
+				config.setType(URI.create(providerNode.get(TYPE).textValue()));
+				config.setJson(providerNode.get(CONFIGURATION));
+				if (config != null)
+					((ConfigurableServiceProvider) instance).configure(config);
+			} catch (Exception e) {
+				throw new DeserializationException(
+						"Could not configure provider " + providerId
+								+ " using bean " + providerNode, e);
+			}
+		return instance;
+	}
+
+	@SuppressWarnings("serial")
+	static class DeserializationException extends Exception {
+		public DeserializationException(String string) {
+			super(string);
+		}
+
+		public DeserializationException(String string, Exception ex) {
+			super(string, ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java
new file mode 100644
index 0000000..9dc3f00
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java
@@ -0,0 +1,652 @@
+/*******************************************************************************
+ * 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.servicedescriptions.impl;
+
+import static java.lang.System.currentTimeMillis;
+import static java.lang.Thread.MIN_PRIORITY;
+import static java.lang.Thread.currentThread;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observer;
+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.ServiceDescriptionsConfiguration;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider.FindServiceDescriptionsCallBack;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.events.AddedProviderEvent;
+import net.sf.taverna.t2.servicedescriptions.events.PartialServiceDescriptionsNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderStatusNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderUpdatingNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderWarningNotification;
+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.servicedescriptions.impl.ServiceDescriptionDeserializer.DeserializationException;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+public class ServiceDescriptionRegistryImpl implements ServiceDescriptionRegistry {
+	/**
+	 * If a writable property of this name on a provider exists (ie. the provider has a method
+	 * setServiceDescriptionRegistry(ServiceDescriptionRegistry registry) - then this property will
+	 * be set to the current registry.
+	 */
+	public static final String SERVICE_DESCRIPTION_REGISTRY = "serviceDescriptionRegistry";
+	public static Logger logger = Logger.getLogger(ServiceDescriptionRegistryImpl.class);
+	public static final ThreadGroup threadGroup = new ThreadGroup("Service description providers");
+	/**
+	 * Total maximum timeout while waiting for description threads to finish
+	 */
+	private static final long DESCRIPTION_THREAD_TIMEOUT_MS = 3000;
+	protected static final String CONF_DIR = "conf";
+	protected static final String SERVICE_PROVIDERS_FILENAME = "service_providers.xml";
+	private static final String DEFAULT_CONFIGURABLE_SERVICE_PROVIDERS_FILENAME = "default_service_providers.xml";
+
+	private ServiceDescriptionsConfiguration serviceDescriptionsConfig;
+	private ApplicationConfiguration applicationConfiguration;
+	/**
+	 * <code>false</code> until first call to {@link #loadServiceProviders()} - which is done by
+	 * first call to {@link #getServiceDescriptionProviders()}.
+	 */
+	private boolean hasLoadedProviders = false;
+	/**
+	 * <code>true</code> while {@link #loadServiceProviders(File)},
+	 * {@link #loadServiceProviders(URL)} or {@link #loadServiceProviders()} is in progress, avoids
+	 * triggering {@link #saveServiceDescriptions()} on
+	 * {@link #addServiceDescriptionProvider(ServiceDescriptionProvider)} calls.
+	 */
+	private boolean loading = false;
+	private MultiCaster<ServiceDescriptionRegistryEvent> observers = new MultiCaster<>(this);
+	private List<ServiceDescriptionProvider> serviceDescriptionProviders;
+	private Set<ServiceDescriptionProvider> allServiceProviders;
+	private Map<ServiceDescriptionProvider, Set<ServiceDescription>> providerDescriptions = new HashMap<>();
+	private Map<ServiceDescriptionProvider, Thread> serviceDescriptionThreads = new HashMap<>();
+	/**
+	 * Service providers added by the user, should be saved
+	 */
+	private Set<ServiceDescriptionProvider> userAddedProviders = new HashSet<>();
+	private Set<ServiceDescriptionProvider> userRemovedProviders = new HashSet<>();
+	private Set<ServiceDescriptionProvider> defaultServiceDescriptionProviders;
+	/**
+	 * File containing a list of configured ConfigurableServiceProviders which is used to get the
+	 * default set of service descriptions together with those provided by AbstractTemplateServiceS.
+	 * This file is located in the conf directory of the Taverna startup directory.
+	 */
+	private File defaultConfigurableServiceProvidersFile;
+	private boolean defaultSystemConfigurableProvidersLoaded = false;
+
+	static {
+		threadGroup.setMaxPriority(MIN_PRIORITY);
+	}
+
+	public ServiceDescriptionRegistryImpl(
+			ApplicationConfiguration applicationConfiguration) {
+		this.applicationConfiguration = applicationConfiguration;
+		defaultConfigurableServiceProvidersFile = new File(
+				getTavernaStartupConfigurationDirectory(),
+				DEFAULT_CONFIGURABLE_SERVICE_PROVIDERS_FILENAME);
+	}
+
+	/**
+	 * Get the Taverna distribution (startup) configuration directory.
+	 */
+	private File getTavernaStartupConfigurationDirectory() {
+		File distroHome = null;
+		File configDirectory = null;
+		distroHome = applicationConfiguration.getStartupDir();
+		configDirectory = new File(distroHome, "conf");
+		if (!configDirectory.exists())
+			configDirectory.mkdir();
+		return configDirectory;
+	}
+
+	private static void joinThreads(Collection<? extends Thread> threads,
+			long descriptionThreadTimeoutMs) {
+		long finishJoinBy = currentTimeMillis() + descriptionThreadTimeoutMs;
+		for (Thread thread : threads) {
+			// No shorter timeout than 1 ms (thread.join(0) waits forever!)
+			long timeout = Math.max(1, finishJoinBy - currentTimeMillis());
+			try {
+				thread.join(timeout);
+			} catch (InterruptedException e) {
+				currentThread().interrupt();
+				return;
+			}
+			if (thread.isAlive())
+				logger.debug("Thread did not finish " + thread);
+		}
+	}
+
+
+	@Override
+	public void addObserver(Observer<ServiceDescriptionRegistryEvent> observer) {
+		observers.addObserver(observer);
+	}
+
+	@Override
+	public void addServiceDescriptionProvider(ServiceDescriptionProvider provider) {
+		synchronized (this) {
+			userRemovedProviders.remove(provider);
+			if (!getDefaultServiceDescriptionProviders().contains(provider))
+				userAddedProviders.add(provider);
+			allServiceProviders.add(provider);
+		}
+
+		// Spring-like auto-config
+		try {
+			// BeanUtils should ignore this if provider does not have that property
+			BeanUtils.setProperty(provider, SERVICE_DESCRIPTION_REGISTRY, this);
+		} catch (IllegalAccessException | InvocationTargetException e) {
+			logger.warn("Could not set serviceDescriptionRegistry on "
+					+ provider, e);
+		}
+
+		if (!loading)
+			saveServiceDescriptions();
+		observers.notify(new AddedProviderEvent(provider));
+		updateServiceDescriptions(false, false);
+	}
+
+	private File findServiceDescriptionsFile() {
+		File confDir = new File(
+				applicationConfiguration.getApplicationHomeDir(), CONF_DIR);
+		confDir.mkdirs();
+		if (!confDir.isDirectory())
+			throw new RuntimeException("Invalid directory: " + confDir);
+		File serviceDescriptionsFile = new File(confDir,
+				SERVICE_PROVIDERS_FILENAME);
+		return serviceDescriptionsFile;
+	}
+
+	@Override
+	public List<Observer<ServiceDescriptionRegistryEvent>> getObservers() {
+		return observers.getObservers();
+	}
+
+	// Fallback to this method that uses hardcoded default services if you cannot read them from
+	// the file.
+//	@SuppressWarnings("unchecked")
+//	public synchronized Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProvidersFallback() {
+//		/*if (defaultServiceDescriptionProviders != null) {
+//	 return defaultServiceDescriptionProviders;
+//	 }
+//	 defaultServiceDescriptionProviders = new HashSet<ServiceDescriptionProvider>();
+//		 */
+//		for (ServiceDescriptionProvider provider : serviceDescriptionProviders) {
+//
+//			/* We do not need these - already loaded them from getDefaultServiceDescriptionProviders()
+//	 if (!(provider instanceof ConfigurableServiceProvider)) {
+//	 defaultServiceDescriptionProviders.add(provider);
+//	 continue;
+//	 }*/
+//
+//			// Just load the hard coded default configurable service providers
+//			if (provider instanceof ConfigurableServiceProvider){
+//				ConfigurableServiceProvider<Object> template = ((ConfigurableServiceProvider<Object>)
+//						provider);
+//				// Get configurations
+//				List<Object> configurables = template.getDefaultConfigurations();
+//				for (Object config : configurables) {
+//					// Make a copy that we can configure
+//					ConfigurableServiceProvider<Object> configurableProvider = template.clone();
+//					try {
+//						configurableProvider.configure(config);
+//					} catch (ConfigurationException e) {
+//						logger.warn("Can't configure provider "
+//								+ configurableProvider + " with " + config);
+//						continue;
+//					}
+//					defaultServiceDescriptionProviders.add(configurableProvider);
+//				}
+//			}
+//		}
+//		return defaultServiceDescriptionProviders;
+//	}
+
+	// Get the default services.
+	@Override
+	public synchronized Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProviders() {
+		if (defaultServiceDescriptionProviders != null)
+			return defaultServiceDescriptionProviders;
+		defaultServiceDescriptionProviders = new HashSet<>();
+
+		/*
+		 * Add default configurable service description providers from the
+		 * default_service_providers.xml file
+		 */
+		if (defaultConfigurableServiceProvidersFile.exists()) {
+			try {
+				ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer(
+						serviceDescriptionProviders);
+				defaultServiceDescriptionProviders.addAll(deserializer
+						.deserializeDefaults(this,
+								defaultConfigurableServiceProvidersFile));
+				/*
+				 * We have successfully loaded the defaults for system
+				 * configurable providers. Note that there are still defaults
+				 * for third party configurable providers, which will be loaded
+				 * below using getDefaultConfigurations().
+				 */
+				defaultSystemConfigurableProvidersLoaded = true;
+			} catch (Exception e) {
+				logger.error("Could not load default service providers from "
+						+ defaultConfigurableServiceProvidersFile.getAbsolutePath(), e);
+
+				/*
+				 * Fallback on the old hardcoded method of loading default
+				 * system configurable service providers using
+				 * getDefaultConfigurations().
+				 */
+				defaultSystemConfigurableProvidersLoaded = false;
+			}
+		} else {
+			logger.warn("Could not find the file "
+					+ defaultConfigurableServiceProvidersFile.getAbsolutePath()
+					+ " containing default system service providers. "
+					+ "Using the hardcoded list of default system providers.");
+
+			/*
+			 * Fallback on the old hardcoded method of loading default system
+			 * configurable service providers using getDefaultConfigurations().
+			 */
+			defaultSystemConfigurableProvidersLoaded = false;
+		}
+
+		/*
+		 * Load other default service description providers - template, local
+		 * workers and third party configurable service providers
+		 */
+		for (ServiceDescriptionProvider provider : serviceDescriptionProviders) {
+			/*
+			 * Template service providers (beanshell, string constant, etc. )
+			 * and providers of local workers.
+			 */
+			if (!(provider instanceof ConfigurableServiceProvider)) {
+				defaultServiceDescriptionProviders.add(provider);
+				continue;
+			}
+
+			/*
+			 * Default system or third party configurable service description
+			 * provider. System ones are read from the
+			 * default_service_providers.xml file so getDefaultConfigurations()
+			 * on them will not have much effect here unless
+			 * defaultSystemConfigurableProvidersLoaded is set to false.
+			 */
+			//FIXME needs to be designed to work using Configuration instances
+			//FIXME needs to get configurations via OSGi discovery
+			/*
+			ConfigurableServiceProvider template = (ConfigurableServiceProvider) provider;
+			// Get configurations
+			for (ObjectNode config : template.getDefaultConfigurations()) {
+				// Make a copy that we can configure
+				ConfigurableServiceProvider configurableProvider = template.clone();
+				try {
+					configurableProvider.configure(config);
+				} catch (ConfigurationException e) {
+					logger.warn("Can't configure provider "
+							+ configurableProvider + " with " + config);
+					continue;
+				}
+				defaultServiceDescriptionProviders.add(configurableProvider);
+			}
+			*/
+		}
+
+		return defaultServiceDescriptionProviders;
+	}
+
+	@Override
+	public synchronized Set<ServiceDescriptionProvider> getServiceDescriptionProviders() {
+		if (allServiceProviders != null)
+			return new HashSet<>(allServiceProviders);
+		allServiceProviders = new HashSet<>(userAddedProviders);
+		synchronized (this) {
+			if (!hasLoadedProviders)
+				try {
+					loadServiceProviders();
+				} catch (Exception e) {
+					logger.error("Could not load service providers", e);
+				} finally {
+					hasLoadedProviders = true;
+				}
+		}
+		for (ServiceDescriptionProvider provider : getDefaultServiceDescriptionProviders()) {
+			if (userRemovedProviders.contains(provider))
+				continue;
+			if (provider instanceof ConfigurableServiceProvider
+					&& !serviceDescriptionsConfig.isIncludeDefaults())
+				// We'll skip the default configurable service provders
+				continue;
+			allServiceProviders.add(provider);
+		}
+		return new HashSet<>(allServiceProviders);
+	}
+
+	@Override
+	public Set<ServiceDescriptionProvider> getServiceDescriptionProviders(
+			ServiceDescription sd) {
+		Set<ServiceDescriptionProvider> result = new HashSet<>();
+		for (ServiceDescriptionProvider sdp : providerDescriptions.keySet())
+			if (providerDescriptions.get(sdp).contains(sd))
+				result.add(sdp);
+		return result;
+	}
+
+	@Override
+	public Set<ServiceDescription> getServiceDescriptions() {
+		updateServiceDescriptions(false, true);
+		Set<ServiceDescription> serviceDescriptions = new HashSet<>();
+		synchronized (providerDescriptions) {
+			for (Set<ServiceDescription> providerDesc : providerDescriptions
+					.values())
+				serviceDescriptions.addAll(providerDesc);
+		}
+		return serviceDescriptions;
+	}
+
+	@Override
+	public ServiceDescription getServiceDescription(URI serviceType) {
+		for (ServiceDescription serviceDescription : getServiceDescriptions())
+			if (serviceDescription.getActivityType().equals(serviceType))
+				return serviceDescription;
+		return null;
+	}
+
+	@Override
+	public List<ConfigurableServiceProvider> getUnconfiguredServiceProviders() {
+		List<ConfigurableServiceProvider> providers = new ArrayList<>();
+		for (ServiceDescriptionProvider provider : serviceDescriptionProviders)
+			if (provider instanceof ConfigurableServiceProvider)
+				providers.add((ConfigurableServiceProvider) provider);
+		return providers;
+	}
+
+	@Override
+	public Set<ServiceDescriptionProvider> getUserAddedServiceProviders() {
+		return new HashSet<>(userAddedProviders);
+	}
+
+	@Override
+	public Set<ServiceDescriptionProvider> getUserRemovedServiceProviders() {
+		return new HashSet<>(userRemovedProviders);
+	}
+
+	@Override
+	public void loadServiceProviders() {
+		File serviceProviderFile = findServiceDescriptionsFile();
+		if (serviceProviderFile.isFile())
+			loadServiceProviders(serviceProviderFile);
+		hasLoadedProviders = true;
+	}
+
+	@Override
+	public void loadServiceProviders(File serviceProvidersFile) {
+		ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer(
+				serviceDescriptionProviders);
+		loading = true;
+		try {
+			deserializer.deserialize(this, serviceProvidersFile);
+		} catch (DeserializationException e) {
+			logger.error("failed to deserialize configuration", e);
+		}
+		loading = false;
+	}
+
+	@Override
+	public void loadServiceProviders(URL serviceProvidersURL) {
+		ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer(
+				serviceDescriptionProviders);
+		loading = true;
+		try {
+			deserializer.deserialize(this, serviceProvidersURL);
+		} catch (DeserializationException e) {
+			logger.error("failed to deserialize configuration", e);
+		}
+		loading = false;
+	}
+
+	@Override
+	public void refresh() {
+		updateServiceDescriptions(true, false);
+	}
+
+	@Override
+	public void removeObserver(Observer<ServiceDescriptionRegistryEvent> observer) {
+		observers.removeObserver(observer);
+	}
+
+	@Override
+	public synchronized void removeServiceDescriptionProvider(
+			ServiceDescriptionProvider provider) {
+		if (!userAddedProviders.remove(provider))
+			// Not previously added - must be a default one.. but should we remove it?
+			if (loading || serviceDescriptionsConfig.isRemovePermanently()
+					&& serviceDescriptionsConfig.isIncludeDefaults())
+				userRemovedProviders.add(provider);
+		if (allServiceProviders.remove(provider)) {
+			synchronized (providerDescriptions) {
+				Thread thread = serviceDescriptionThreads.remove(provider);
+				if (thread != null)
+					thread.interrupt();
+				providerDescriptions.remove(provider);
+			}
+			observers.notify(new RemovedProviderEvent(provider));
+		}
+		if (!loading)
+			saveServiceDescriptions();
+	}
+
+	@Override
+	public void saveServiceDescriptions() {
+		File serviceDescriptionsFile = findServiceDescriptionsFile();
+		saveServiceDescriptions(serviceDescriptionsFile);
+	}
+
+	@Override
+	public void saveServiceDescriptions(File serviceDescriptionsFile) {
+		ServiceDescriptionSerializer serializer = new ServiceDescriptionSerializer();
+		try {
+			serializer.serializeRegistry(this, serviceDescriptionsFile);
+		} catch (IOException e) {
+			throw new RuntimeException("Can't save service descriptions to "
+					+ serviceDescriptionsFile);
+		}
+	}
+
+	/**
+	 * Exports all configurable service providers (that give service
+	 * descriptions) currently found in the Service Registry (apart from service
+	 * templates and local services) regardless of who added them (user or
+	 * default system providers).
+	 * <p>
+	 * Unlike {@link #saveServiceDescriptions}, this export does not have the
+	 * "ignored providers" section as this is just a plain export of everything
+	 * in the Service Registry.
+	 * 
+	 * @param serviceDescriptionsFile
+	 */
+	@Override
+	public void exportCurrentServiceDescriptions(File serviceDescriptionsFile) {
+		ServiceDescriptionSerializer serializer = new ServiceDescriptionSerializer();
+		try {
+			serializer.serializeFullRegistry(this, serviceDescriptionsFile);
+		} catch (IOException e) {
+			throw new RuntimeException("Could not save service descriptions to "
+					+ serviceDescriptionsFile);
+		}
+	}
+
+	public void setServiceDescriptionProvidersList(
+			List<ServiceDescriptionProvider> serviceDescriptionProviders) {
+		this.serviceDescriptionProviders = serviceDescriptionProviders;
+	}
+
+	private void updateServiceDescriptions(boolean refreshAll, boolean waitFor) {
+		List<Thread> threads = new ArrayList<>();
+		for (ServiceDescriptionProvider provider : getServiceDescriptionProviders()) {
+			synchronized (providerDescriptions) {
+				if (providerDescriptions.containsKey(provider) && !refreshAll)
+					// We'll used the cached values
+					continue;
+				Thread oldThread = serviceDescriptionThreads.get(provider);
+				if (oldThread != null && oldThread.isAlive()) {
+					if (refreshAll)
+						// New thread will override the old thread
+						oldThread.interrupt();
+					else {
+						// observers.notify(new ProviderStatusNotification(provider, "Waiting for provider"));
+						continue;
+					}
+				}
+				// Not run yet - we'll start a new tread
+				Thread thread = new FindServiceDescriptionsThread(provider);
+				threads.add(thread);
+				serviceDescriptionThreads.put(provider, thread);
+				thread.start();
+			}
+		}
+		if (waitFor)
+			joinThreads(threads, DESCRIPTION_THREAD_TIMEOUT_MS);
+	}
+
+	@Override
+	public boolean isDefaultSystemConfigurableProvidersLoaded() {
+		return defaultSystemConfigurableProvidersLoaded;
+	}
+
+	/**
+	 * Sets the serviceDescriptionsConfig.
+	 * 
+	 * @param serviceDescriptionsConfig
+	 *            the new value of serviceDescriptionsConfig
+	 */
+	public void setServiceDescriptionsConfig(
+			ServiceDescriptionsConfiguration serviceDescriptionsConfig) {
+		this.serviceDescriptionsConfig = serviceDescriptionsConfig;
+	}
+
+	class FindServiceDescriptionsThread extends Thread implements
+			UncaughtExceptionHandler, FindServiceDescriptionsCallBack {
+		private final ServiceDescriptionProvider provider;
+		private boolean aborting = false;
+		private final Set<ServiceDescription> providerDescs = new HashSet<>();
+
+		FindServiceDescriptionsThread(ServiceDescriptionProvider provider) {
+			super(threadGroup, "Find service descriptions from " + provider);
+			this.provider = provider;
+			setUncaughtExceptionHandler(this);
+			setDaemon(true);
+		}
+
+		@Override
+		public void fail(String message, Throwable ex) {
+			logger.warn("Provider " + getProvider() + ": " + message, ex);
+			if (aborting)
+				return;
+			observers.notify(new ProviderErrorNotification(getProvider(),
+					message, ex));
+		}
+
+		@Override
+		public void finished() {
+			if (aborting)
+				return;
+			synchronized (providerDescriptions) {
+				providerDescriptions.put(getProvider(), providerDescs);
+			}
+			observers.notify(new ServiceDescriptionProvidedEvent(getProvider(),
+					providerDescs));
+		}
+
+		@Override
+		public void partialResults(
+				Collection<? extends ServiceDescription> serviceDescriptions) {
+			if (aborting)
+				return;
+			providerDescs.addAll(serviceDescriptions);
+			synchronized (providerDescriptions) {
+				providerDescriptions.put(getProvider(), providerDescs);
+			}
+			observers.notify(new PartialServiceDescriptionsNotification(
+					getProvider(), serviceDescriptions));
+		}
+
+		@Override
+		public void status(String message) {
+			logger.debug("Provider " + getProvider() + ": " + message);
+			if (aborting)
+				return;
+			observers.notify(new ProviderStatusNotification(getProvider(),
+					message));
+		}
+
+		@Override
+		public void warning(String message) {
+			logger.warn("Provider " + getProvider() + ": " + message);
+			if (aborting)
+				return;
+			observers.notify(new ProviderWarningNotification(getProvider(),
+					message));
+		}
+
+		public ServiceDescriptionProvider getProvider() {
+			return provider;
+		}
+
+		@Override
+		public void interrupt() {
+			aborting = true;
+			super.interrupt();
+		}
+
+		@Override
+		public void run() {
+			observers.notify(new ProviderUpdatingNotification(provider));
+			getProvider().findServiceDescriptionsAsync(this);
+		}
+
+		@Override
+		public void uncaughtException(Thread t, Throwable ex) {
+			logger.error("Uncaught exception in " + t, ex);
+			fail("Uncaught exception", ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java
new file mode 100644
index 0000000..8a047a3
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java
@@ -0,0 +1,102 @@
+package net.sf.taverna.t2.servicedescriptions.impl;
+
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.CONFIGURATION;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.IGNORED;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDERS;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDER_ID;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.SERVICE_PANEL_CONFIGURATION;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.TYPE;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Set;
+
+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;
+import org.jdom.JDOMException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+class ServiceDescriptionSerializer {
+	private static Logger logger = Logger
+			.getLogger(ServiceDescriptionSerializer.class);
+
+	public void serializeRegistry(ServiceDescriptionRegistry registry, File file)
+			throws IOException {
+		Set<ServiceDescriptionProvider> ignoreProviders = registry
+				.getUserRemovedServiceProviders();
+		JsonNode registryElement = serializeRegistry(registry, ignoreProviders);
+		try (BufferedOutputStream bufferedOutStream = new BufferedOutputStream(
+				new FileOutputStream(file))) {
+			bufferedOutStream.write(registryElement.toString()
+					.getBytes("UTF-8"));
+		}
+	}
+
+	/**
+	 * Export the whole service registry to an xml file, regardless of who added
+	 * the service provider (user or system default). In this case there will be
+	 * no "ignored providers" in the saved file.
+	 */
+	public void serializeFullRegistry(ServiceDescriptionRegistry registry,
+			File file) throws IOException {
+		JsonNode registryElement = serializeRegistry(registry, ALL_PROVIDERS);
+		try (BufferedOutputStream bufferedOutStream = new BufferedOutputStream(
+				new FileOutputStream(file))) {
+			bufferedOutStream.write(registryElement.toString()
+					.getBytes("UTF-8"));
+		}
+	}
+
+	private static final JsonNodeFactory factory = JsonNodeFactory.instance;
+	private static final Set<ServiceDescriptionProvider> ALL_PROVIDERS = null;
+
+	private JsonNode serializeRegistry(ServiceDescriptionRegistry registry,
+			Set<ServiceDescriptionProvider> ignoreProviders) {
+		ObjectNode overallConfiguration = factory.objectNode();
+		overallConfiguration.put(SERVICE_PANEL_CONFIGURATION,
+				ignoreProviders != ALL_PROVIDERS ? "full" : "defaults only");
+		ArrayNode providers = overallConfiguration.putArray(PROVIDERS);
+
+		for (ServiceDescriptionProvider provider : registry
+				.getUserAddedServiceProviders())
+			try {
+				providers.add(serializeProvider(provider));
+			} catch (JDOMException | IOException e) {
+				logger.warn("Could not serialize " + provider, e);
+			}
+
+		if (ignoreProviders != ALL_PROVIDERS) {
+			ArrayNode ignored = overallConfiguration.putArray(IGNORED);
+			for (ServiceDescriptionProvider provider : ignoreProviders)
+				try {
+					ignored.add(serializeProvider(provider));
+				} catch (JDOMException | IOException e) {
+					logger.warn("Could not serialize " + provider, e);
+				}
+		}
+
+		return overallConfiguration;
+	}
+
+	private JsonNode serializeProvider(ServiceDescriptionProvider provider)
+			throws JDOMException, IOException {
+		ObjectNode node = factory.objectNode();
+		node.put(PROVIDER_ID, provider.getId());
+
+		if (provider instanceof ConfigurableServiceProvider) {
+			ConfigurableServiceProvider configurable = (ConfigurableServiceProvider) provider;
+			node.put(TYPE, configurable.getConfiguration().getType().toString());
+			node.put(CONFIGURATION, configurable.getConfiguration().getJson());
+		}
+		return node;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java
new file mode 100644
index 0000000..ee180a7
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java
@@ -0,0 +1,15 @@
+package net.sf.taverna.t2.servicedescriptions.impl;
+
+import org.jdom.Namespace;
+
+public interface ServiceDescriptionXMLConstants {
+
+	public static final Namespace SERVICE_DESCRIPTION_NS = Namespace
+			.getNamespace("http://taverna.sf.net/2009/xml/servicedescription");
+	public static final String PROVIDER = "provider";
+	public static final String PROVIDERS = "providers";
+	public static final String SERVICE_DESCRIPTIONS = "serviceDescriptions";
+	public static final String IGNORED_PROVIDERS = "ignoredProviders";
+	public static final String PROVIDER_IDENTIFIER = "providerId";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java
new file mode 100644
index 0000000..ef0295c
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * 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.servicedescriptions.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+
+public class ServiceDescriptionsConfigurationImpl extends AbstractConfigurable
+		implements ServiceDescriptionsConfiguration {
+	private static final String INCLUDE_DEFAULTS = "includeDefaults";
+	private static final String SERVICE_PALETTE = "Service providers";
+	private static final String SERVICE_PALETTE_PREFIX = "ServiceProviders";
+	private static final String CATEGORY = "Services";
+	private static final String UUID = "f0d1ef24-9337-412f-b2c3-220a01e2efd0";
+	private static final String REMOVE_PERMANENTLY = "removePermanently";
+
+	public ServiceDescriptionsConfigurationImpl(
+			ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	@Override
+	public String getCategory() {
+		return CATEGORY;
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		Map<String, String> defaults = new HashMap<String, String>();
+		defaults.put(INCLUDE_DEFAULTS, "true");
+		defaults.put(REMOVE_PERMANENTLY, "true");
+		return defaults;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return SERVICE_PALETTE;
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return SERVICE_PALETTE_PREFIX;
+	}
+
+	@Override
+	public String getUUID() {
+		return UUID;
+	}
+
+	@Override
+	public boolean isIncludeDefaults() {
+		return Boolean.parseBoolean(getProperty(INCLUDE_DEFAULTS));
+	}
+
+	@Override
+	public void setIncludeDefaults(boolean includeDefaults) {
+		setProperty(INCLUDE_DEFAULTS, Boolean.toString(includeDefaults));
+	}
+
+	@Override
+	public boolean isRemovePermanently() {
+		return Boolean.parseBoolean(getProperty(REMOVE_PERMANENTLY));
+	}
+
+	@Override
+	public void setRemovePermanently(boolean removePermanently) {
+		setProperty(REMOVE_PERMANENTLY, Boolean.toString(removePermanently));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java
new file mode 100644
index 0000000..46f82c4
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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.activitypalette;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+
+public class ActivityPaletteConfiguration extends AbstractConfigurable {
+	private Map<String,String> defaultPropertyMap;
+
+	public ActivityPaletteConfiguration(ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	@Override
+	public String getCategory() {
+		return "Services";
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		if (defaultPropertyMap == null) {
+			defaultPropertyMap = new HashMap<>();
+
+			// //wsdl
+			//defaultPropertyMap.put("taverna.defaultwsdl", "http://www.ebi.ac.uk/xembl/XEMBL.wsdl,"+
+			//                    "http://soap.genome.jp/KEGG.wsdl,"+
+			//                    "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/soap/eutils.wsdl,"+
+			//                    "http://soap.bind.ca/wsdl/bind.wsdl,"+
+			//                    "http://www.ebi.ac.uk/ws/services/urn:Dbfetch?wsdl");
+
+			// //soaplab
+			//defaultPropertyMap.put("taverna.defaultsoaplab", "http://www.ebi.ac.uk/soaplab/services/");
+
+			// //biomart
+			//defaultPropertyMap.put("taverna.defaultmartregistry","http://www.biomart.org/biomart");
+
+			//add property names
+			//defaultPropertyMap.put("name.taverna.defaultwsdl", "WSDL");
+			//defaultPropertyMap.put("name.taverna.defaultsoaplab","Soaplab");
+			//defaultPropertyMap.put("name.taverna.defaultmartregistry", "Biomart");
+		}
+		return defaultPropertyMap;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return "Activity Palette";
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return "ActivityPalette";
+	}
+
+	@Override
+	public String getUUID() {
+		return "ad9f3a60-5967-11dd-ae16-0800200c9a66";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java
new file mode 100644
index 0000000..6166e60
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * 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.activitypalette;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.EAST;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.FlowLayout.LEFT;
+import static java.awt.FlowLayout.RIGHT;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showInputDialog;
+import static javax.swing.border.BevelBorder.LOWERED;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.BoxLayout;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.border.BevelBorder;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public class ActivityPaletteConfigurationPanel extends JPanel {
+	private static Logger logger = Logger
+			.getLogger(ActivityPaletteConfigurationPanel.class);
+
+	private Map<String,List<String>> values = new HashMap<>();
+	private Map<String,String> names = new HashMap<>();
+	private DefaultComboBoxModel<String> model;
+	private DefaultListModel<String> listModel;
+	private JList<String> propertyListItems;
+	private String selectedKey;
+	private JButton deleteTypeButton;
+	private final ActivityPaletteConfiguration config;
+
+	public ActivityPaletteConfigurationPanel(ActivityPaletteConfiguration config) {
+		super(new BorderLayout());
+		this.config = config;
+
+		model = new DefaultComboBoxModel<>();
+		for (String key : config.getInternalPropertyMap().keySet()) {
+			if (key.startsWith("taverna.")
+					&& config.getPropertyStringList(key) != null) {
+				model.addElement(key);
+				values.put(key,
+						new ArrayList<>(config.getPropertyStringList(key)));
+			}
+			if (key.startsWith("name.taverna."))
+				names.put(key, config.getProperty(key).toString());
+		}
+		deleteTypeButton = new JButton("Delete");
+
+		final JButton addTypeButton = new JButton("Add");
+		final JComboBox<String> comboBox = new JComboBox<>(model);
+		comboBox.setRenderer(new DefaultListCellRenderer() {
+			@Override
+			public Component getListCellRendererComponent(JList<?> list,
+					Object value, int index, boolean isSelected,
+					boolean cellHasFocus) {
+				if (value != null && value instanceof String) {
+					String name = names.get("name." + value);
+					if (name != null)
+						value = name;
+				}
+				return super.getListCellRendererComponent(list, value, index,
+						isSelected, cellHasFocus);
+			}
+		});
+
+		deleteTypeButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				String displayText = names.get("name." + selectedKey);
+				if (displayText == null)
+					displayText = selectedKey;
+				if (confirm("Confirm removal",
+						"Are you sure you wish to remove the type "
+								+ displayText + "?")) {
+					names.remove("name." + selectedKey);
+					values.remove(selectedKey);
+					model.removeElement(selectedKey);
+					comboBox.setSelectedIndex(0);
+				}
+			}
+		});
+
+		addTypeButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				String key = input("New key", "Provide the new key.");
+				if (key == null)
+					return;
+				String name = input("Name for the key",
+						"Provide the name for the key: " + key);
+				if (name == null)
+					return;
+
+				values.put(key, new ArrayList<String>());
+				names.put("name." + key, name);
+				model.addElement(key);
+				comboBox.setSelectedItem(key);
+			}
+		});
+
+		comboBox.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				if (comboBox.getSelectedItem() != null
+						&& comboBox.getSelectedItem() instanceof String) {
+					selectedKey = (String) comboBox.getSelectedItem();
+					List<String> selectedList = values.get(selectedKey);
+					populateList(selectedList);
+					deleteTypeButton.setEnabled(selectedList.size() == 0);
+				}
+			}
+		});
+
+		JPanel propertySelectionPanel = new JPanel(new FlowLayout(LEFT));
+		propertySelectionPanel.add(new JLabel("Activity type:"));
+		propertySelectionPanel.add(comboBox);
+		propertySelectionPanel.add(addTypeButton);
+		propertySelectionPanel.add(deleteTypeButton);
+		add(propertySelectionPanel, NORTH);
+
+		JPanel listPanel = new JPanel(new BorderLayout());
+		listModel = new DefaultListModel<>();
+		propertyListItems = new JList<>(listModel);
+		propertyListItems.setBorder(new BevelBorder(LOWERED));
+
+		listPanel.add(propertyListItems, CENTER);
+		listPanel.add(listButtons(), EAST);
+
+		add(listPanel, CENTER);
+
+		add(applyButtonPanel(), SOUTH);
+
+		if (model.getSize() > 0)
+			comboBox.setSelectedItem(model.getElementAt(0));
+	}
+
+	private void populateList(List<String> selectedList) {
+		listModel.removeAllElements();
+		for (String item : selectedList)
+			listModel.addElement(item);
+	}
+
+	private JPanel applyButtonPanel() {
+		JPanel applyPanel = new JPanel(new FlowLayout(RIGHT));
+		JButton applyButton = new JButton("Apply");
+
+		applyButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				config.getInternalPropertyMap().clear();
+				for (String key : values.keySet()) {
+					List<String> properties = values.get(key);
+					config.setPropertyStringList(key, new ArrayList<>(
+							properties));
+				}
+				for (String key : names.keySet())
+					config.setProperty(key, names.get(key));
+				store();
+			}
+		});
+
+		applyPanel.add(applyButton);
+		return applyPanel;
+	}
+
+	private void store() {
+		try {
+			//FIXME
+			//ConfigurationManager.getInstance().store(config);
+		} catch (Exception e1) {
+			logger.error("There was an error storing the configuration:"
+					+ config.getFilePrefix() + " (UUID=" + config.getUUID()
+					+ ")", e1);
+		}
+	}
+
+	private JPanel listButtons() {
+		JPanel panel = new JPanel();
+		panel.setLayout(new BoxLayout(panel, Y_AXIS));
+		JButton addButton = new JButton("+");
+		addButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				String value = input("New property", "Provide new value for: "
+						+ selectedKey);
+				if (value != null) {
+					listModel.addElement(value);
+					values.get(selectedKey).add(value);
+					deleteTypeButton.setEnabled(false);
+				}
+			}
+		});
+
+		JButton deleteButton = new JButton("-");
+		deleteButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				Object value = propertyListItems.getSelectedValue();
+				if (confirm("Confirm removal",
+						"Are you sure you wish to remove " + value + "?")) {
+					listModel.removeElement(value);
+					values.get(selectedKey).remove(value);
+					if (values.get(selectedKey).size() == 0)
+						deleteTypeButton.setEnabled(true);
+				}
+			}
+		});
+
+		panel.add(addButton);
+		panel.add(deleteButton);
+
+		return panel;
+	}
+
+	private boolean confirm(String title, String message) {
+		return showConfirmDialog(this, message, title, YES_NO_OPTION,
+				WARNING_MESSAGE) == YES_OPTION;
+	}
+
+	private String input(String title, String message) {
+		return showInputDialog(this, message, title, INFORMATION_MESSAGE);
+	}
+
+/*	private JButton getAddTypeButton() {
+		JButton result = new JButton("Add");
+		result.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				String val = input("New property value","New property value");
+				if (val!=null) {
+					if (values.get(val) == null) {
+						model.addElement(val);
+						values.put(val, new ArrayList<String>());
+					} else
+						showMessageDialog(ActivityPaletteConfigurationPanel.this, "This property already exists");
+				}
+			}
+		});
+		return result;
+	}
+*/
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java
new file mode 100644
index 0000000..39c4a5a
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.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.activitypalette;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+
+public class ActivityPaletteConfigurationUIFactory implements
+		ConfigurationUIFactory {
+	private ActivityPaletteConfiguration activityPaletteConfiguration;
+
+	@Override
+	public boolean canHandle(String uuid) {
+		return uuid != null && uuid.equals(getConfigurable().getUUID());
+	}
+
+	@Override
+	public Configurable getConfigurable() {
+		return activityPaletteConfiguration;
+	}
+
+	@Override
+	public JPanel getConfigurationPanel() {
+		return new ActivityPaletteConfigurationPanel(
+				activityPaletteConfiguration);
+	}
+
+	public void setActivityPaletteConfiguration(
+			ActivityPaletteConfiguration activityPaletteConfiguration) {
+		this.activityPaletteConfiguration = activityPaletteConfiguration;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
new file mode 100644
index 0000000..6c9fed9
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@
+#net.sf.taverna.t2.workbench.ui.activitypalette.ActivityPaletteConfigurationUIFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml
new file mode 100644
index 0000000..34921f5
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/osgi
+                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+	<service ref="ServiceDescriptionRegistryImpl" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry"/>
+	<service ref="ServiceDescriptionsConfigurationImpl" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration"/>
+
+	<reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" />
+	<reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" />
+
+	<list id="serviceDescriptionProviders" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider" cardinality="0..N" greedy-proxying="true"/>
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml
new file mode 100644
index 0000000..9f7110f
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean name="ServiceDescriptionRegistryImpl" class="net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionRegistryImpl">
+		<constructor-arg name="applicationConfiguration" ref="applicationConfiguration" />
+		<property name="serviceDescriptionProvidersList" ref="serviceDescriptionProviders" />
+		<property name="serviceDescriptionsConfig">
+		 	<ref local="ServiceDescriptionsConfigurationImpl"/>
+		</property>
+	</bean>
+
+	<bean id="ServiceDescriptionsConfigurationImpl" class="net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionsConfigurationImpl">
+		<constructor-arg ref="configurationManager"/>
+	</bean>
+
+	<!-- Don't think ActivityPalette is still used -->
+	<!-- <bean id="ActivityPaletteConfiguration" class="net.sf.taverna.t2.workbench.ui.activitypalette.ActivityPaletteConfiguration">
+		<constructor-arg ref="configurationManager"/>
+	</bean> -->
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java b/taverna-workbench-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java
new file mode 100644
index 0000000..081a9af
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * 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.activitypalette;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uk.org.taverna.configuration.app.impl.ApplicationConfigurationImpl;
+import uk.org.taverna.configuration.impl.ConfigurationManagerImpl;
+
+public class ActivityPaletteConfigurationTest {
+
+	private ActivityPaletteConfiguration conf;
+	private ConfigurationManagerImpl manager;
+
+	@Before
+	public void setup() {
+		File f = new File(System.getProperty("java.io.tmpdir"));
+		final File d = new File(f,UUID.randomUUID().toString());
+		d.mkdir();
+		manager = new ConfigurationManagerImpl(new ApplicationConfigurationImpl() {
+			@Override
+			public File getApplicationHomeDir() {
+				return d;
+			}
+		});
+		conf=new ActivityPaletteConfiguration(manager);
+		conf.restoreDefaults();
+	}
+
+	@Test
+	public void testEmptyList() throws Exception {
+		conf.setPropertyStringList("list", new ArrayList<String>());
+		assertTrue("Result was not a list but was:"+conf.getProperty("list"),conf.getPropertyStringList("list") instanceof List);
+		assertTrue("Result was not a list but was:"+conf.getPropertyStringList("list"),conf.getPropertyStringList("list") instanceof List);
+		List<String> list = conf.getPropertyStringList("list");
+		assertEquals("There should be 0 elements",0,list.size());
+	}
+
+	@Test
+	public void testSingleItem() throws Exception {
+		List<String> list = new ArrayList<>();
+		list.add("fred");
+		conf.setPropertyStringList("single", list);
+
+		assertTrue("should be an ArrayList",conf.getPropertyStringList("single") instanceof List);
+		List<String> l = conf.getPropertyStringList("single");
+		assertEquals("There should be 1 element",1,l.size());
+		assertEquals("Its value should be fred","fred",l.get(0));
+	}
+
+	@Test
+	public void testList() throws Exception {
+		List<String> list = new ArrayList<>();
+		list.add("fred");
+		list.add("bloggs");
+		conf.setPropertyStringList("list", list);
+
+		assertTrue("should be an ArrayList",conf.getPropertyStringList("list") instanceof List);
+		List<String> l = conf.getPropertyStringList("list");
+		assertEquals("There should be 1 element",2,l.size());
+		assertEquals("Its value should be fred","fred",l.get(0));
+		assertEquals("Its value should be bloggs","bloggs",l.get(1));
+	}
+
+	@Test
+	public void testNull() throws Exception {
+		assertNull("Should return null",conf.getProperty("blah blah blah"));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI
new file mode 100644
index 0000000..9f3c02d
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.partition.DummyPartitionAlgorithmSet
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI
new file mode 100644
index 0000000..f5e7226
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI
@@ -0,0 +1,3 @@
+net.sf.taverna.t2.partition.DummyExtractor1
+net.sf.taverna.t2.partition.DummyExtractor2
+net.sf.taverna.t2.partition.DummyExtractor3
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory
----------------------------------------------------------------------
diff --git a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory
new file mode 100644
index 0000000..2d26d31a
--- /dev/null
+++ b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory
@@ -0,0 +1,2 @@
+net.sf.taverna.t2.partition.DummyActivityQueryFactory
+net.sf.taverna.t2.partition.DummyQueryFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-configuration-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-configuration-impl/pom.xml b/taverna-workbench-configuration-impl/pom.xml
new file mode 100644
index 0000000..19356bb
--- /dev/null
+++ b/taverna-workbench-configuration-impl/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>net.sf.taverna.t2</groupId>
+		<artifactId>ui-impl</artifactId>
+		<version>2.0-SNAPSHOT</version>
+	</parent>
+	<groupId>net.sf.taverna.t2.ui-impl</groupId>
+	<artifactId>configuration-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Configuration Management Implementations</name>
+	<description>General configuration management</description>
+
+	<dependencies>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>menu-api</artifactId>
+			<version>${t2.ui.api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>helper-api</artifactId>
+			<version>${t2.ui.api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>configuration-api</artifactId>
+			<version>${t2.ui.api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-configuration-impl</artifactId>
+			<version>0.1.1-SNAPSHOT</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-app-configuration-impl</artifactId>
+			<version>0.1.1-SNAPSHOT</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java
new file mode 100644
index 0000000..0e63a4a
--- /dev/null
+++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * 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.impl.configuration;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+/**
+ * An implementation of Configurable for general Workbench configuration
+ * properties
+ * 
+ * @author Stuart Owen
+ * @author Stian Soiland-Reyes
+ */
+public class WorkbenchConfigurationImpl extends AbstractConfigurable implements
+		WorkbenchConfiguration {
+	private static Logger logger = Logger
+			.getLogger(WorkbenchConfiguration.class);
+	private static final int DEFAULT_MAX_MENU_ITEMS = 20;
+	public static final String TAVERNA_DOTLOCATION = "taverna.dotlocation";
+	public static final String MAX_MENU_ITEMS = "taverna.maxmenuitems";
+	public static final String WARN_INTERNAL_ERRORS = "taverna.warninternal";
+	public static final String CAPTURE_CONSOLE = "taverna.captureconsole";
+	private static final String BIN = "bin";
+	private static final String BUNDLE_CONTENTS = "Contents";
+	private static final String BUNDLE_MAC_OS = "MacOS";
+	private static final String DOT_EXE = "dot.exe";
+	private static final String DOT_FALLBACK = "dot";
+	public static String uuid = "c14856f0-5967-11dd-ae16-0800200c9a66";
+	private static final String MAC_OS_X = "Mac OS X";
+	private static final String WIN32I386 = "win32i386";
+	private static final String WINDOWS = "Windows";
+
+	private ApplicationConfiguration applicationConfiguration;
+
+	/**
+	 * Constructs a new <code>WorkbenchConfigurationImpl</code>.
+	 * 
+	 * @param configurationManager
+	 */
+	public WorkbenchConfigurationImpl(ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	Map<String, String> defaultWorkbenchProperties = null;
+	Map<String, String> workbenchProperties = new HashMap<String, String>();
+
+	@Override
+	public String getCategory() {
+		return "general";
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		if (defaultWorkbenchProperties == null) {
+			defaultWorkbenchProperties = new HashMap<>();
+			String dotLocation = System.getProperty(TAVERNA_DOTLOCATION) != null ? System
+					.getProperty(TAVERNA_DOTLOCATION) : getDefaultDotLocation();
+			if (dotLocation != null)
+				defaultWorkbenchProperties
+						.put(TAVERNA_DOTLOCATION, dotLocation);
+			defaultWorkbenchProperties.put(MAX_MENU_ITEMS,
+					Integer.toString(DEFAULT_MAX_MENU_ITEMS));
+			defaultWorkbenchProperties.put(WARN_INTERNAL_ERRORS,
+					Boolean.FALSE.toString());
+			defaultWorkbenchProperties.put(CAPTURE_CONSOLE,
+					Boolean.TRUE.toString());
+		}
+		return defaultWorkbenchProperties;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return "Workbench";
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return "Workbench";
+	}
+
+	@Override
+	public String getUUID() {
+		return uuid;
+	}
+
+	@Override
+	public boolean getWarnInternalErrors() {
+		String property = getProperty(WARN_INTERNAL_ERRORS);
+		return Boolean.parseBoolean(property);
+	}
+
+	@Override
+	public boolean getCaptureConsole() {
+		String property = getProperty(CAPTURE_CONSOLE);
+		return Boolean.parseBoolean(property);
+	}
+
+	@Override
+	public void setWarnInternalErrors(boolean warnInternalErrors) {
+		setProperty(WARN_INTERNAL_ERRORS, Boolean.toString(warnInternalErrors));
+	}
+
+	@Override
+	public void setCaptureConsole(boolean captureConsole) {
+		setProperty(CAPTURE_CONSOLE, Boolean.toString(captureConsole));
+	}
+
+	@Override
+	public void setMaxMenuItems(int maxMenuItems) {
+		if (maxMenuItems < 2)
+			throw new IllegalArgumentException(
+					"Maximum menu items must be at least 2");
+		setProperty(MAX_MENU_ITEMS, Integer.toString(maxMenuItems));
+	}
+
+	@Override
+	public int getMaxMenuItems() {
+		String property = getProperty(MAX_MENU_ITEMS);
+		try {
+			int maxMenuItems = Integer.parseInt(property);
+			if (maxMenuItems >= 2)
+				return maxMenuItems;
+			logger.warn(MAX_MENU_ITEMS + " can't be less than 2");
+		} catch (NumberFormatException ex) {
+			logger.warn("Invalid number for " + MAX_MENU_ITEMS + ": "
+					+ property);
+		}
+		// We'll return the default instead
+		return DEFAULT_MAX_MENU_ITEMS;
+	}
+
+	@Override
+	public String getDotLocation() {
+		return getProperty(TAVERNA_DOTLOCATION);
+	}
+
+	@Override
+	public void setDotLocation(String dotLocation) {
+		setProperty(TAVERNA_DOTLOCATION, dotLocation);
+	}
+
+	private String getDefaultDotLocation() {
+		if (applicationConfiguration == null)
+			return null;
+		File startupDir = applicationConfiguration.getStartupDir();
+		if (startupDir == null)
+			return DOT_FALLBACK;
+
+		String os = System.getProperty("os.name");
+		if (os.equals(MAC_OS_X))
+			if (startupDir.getParentFile() != null) {
+				File contentsDir = startupDir.getParentFile().getParentFile();
+				if (contentsDir != null
+						&& contentsDir.getName().equalsIgnoreCase(
+								BUNDLE_CONTENTS)) {
+					File dot = new File(new File(contentsDir, BUNDLE_MAC_OS),
+							DOT_FALLBACK);
+					if (dot.exists())
+						return dot.getAbsolutePath();
+				}
+			} else if (os.startsWith(WINDOWS)) {
+				File binWin386Dir = new File(new File(startupDir, BIN),
+						WIN32I386);
+				File dot = new File(binWin386Dir, DOT_EXE);
+				if (dot.exists())
+					return dot.getAbsolutePath();
+			}
+		return DOT_FALLBACK;
+	}
+
+	/**
+	 * Sets the applicationConfiguration.
+	 * 
+	 * @param applicationConfiguration
+	 *            the new value of applicationConfiguration
+	 */
+	public void setApplicationConfiguration(
+			ApplicationConfiguration applicationConfiguration) {
+		this.applicationConfiguration = applicationConfiguration;
+		defaultWorkbenchProperties = null;
+	}
+}