| /******************************************************************************* |
| * 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 org.apache.taverna.lang.observer.MultiCaster; |
| import org.apache.taverna.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); |
| } |
| } |
| } |