blob: 1c7c3190b7512c13f30d5ff5276756c703be95c7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.ptp.services.core;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.Map.Entry;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ptp.services.core.messages.Messages;
import org.eclipse.ptp.services.internal.core.Service;
import org.eclipse.ptp.services.internal.core.ServiceConfiguration;
import org.eclipse.ptp.services.internal.core.ServiceModelEvent;
import org.eclipse.ptp.services.internal.core.ServiceModelEventManager;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.XMLMemento;
/**
* A singleton class which is the entry point to a service model which represents:
* - the set of contributed services
* - the set of providers which provide those services
* - the service fProjectConfigurations for each project which specify which services are
* mapped to which providers.
*
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as
* part of a work in progress. There is no guarantee that this API will work or
* that it will remain the same. Please do not use this API without consulting
* with the RDT team.
*
*/
public class ServiceModelManager extends PlatformObject implements IServiceModelManager {
private final static String SERVICE_EXTENSION_ID = "services"; //$NON-NLS-1$
private final static String PROVIDER_EXTENSION_ID = "providers"; //$NON-NLS-1$
private final static String SERVICE_ELEMENT_NAME = "service"; //$NON-NLS-1$
private final static String NATURE_ELEMENT_NAME = "nature"; //$NON-NLS-1$
private final static String PROVIDER_ELEMENT_NAME = "provider"; //$NON-NLS-1$
private final static String ATTR_ID = "id"; //$NON-NLS-1$
private final static String ATTR_NAME = "name"; //$NON-NLS-1$
private final static String ATTR_PRIORITY = "priority"; //$NON-NLS-1$
private final static String ATTR_SERVICE_ID = "serviceId"; //$NON-NLS-1$
private final static String ATTR_CLASS = "class"; //$NON-NLS-1$
private final static String ATTR_ACTIVE = "active"; //$NON-NLS-1$
private final static String SERVICE_MODEL_ELEMENT_NAME = "service-model"; //$NON-NLS-1$
private final static String PROJECT_ELEMENT_NAME = "project"; //$NON-NLS-1$
private final static String SERVICE_CONFIGURATION_ELEMENT_NAME = "service-configuration"; //$NON-NLS-1$
private final static String PROVIDER_CONFIGURATION_ELEMENT_NAME = "provider-configuration"; //$NON-NLS-1$
private final static String ATTR_PROVIDER_ID = "provider-id"; //$NON-NLS-1$
private final static String DEFAULT_SAVE_FILE_NAME = "service_model.xml"; //$NON-NLS-1$
private static <T> T getConf(Map<IProject, T> map, IProject project) {
if(project == null) {
throw new NullPointerException();
}
T value = map.get(project);
if(value == null) {
throw new ProjectNotConfiguredException();
}
return value;
}
private static void saveModelConfiguration(Map<String, IServiceConfiguration> configs,
Map<IProject, Map<String, IServiceConfiguration>> projectConfigs,
Map<IProject, IServiceConfiguration> activeConfigs,
Writer writer) throws IOException {
XMLMemento rootMemento = XMLMemento.createWriteRoot(SERVICE_MODEL_ELEMENT_NAME);
for (IServiceConfiguration config : configs.values()) {
String configurationId = config.getId();
String configurationName = config.getName();
IMemento configMemento = rootMemento.createChild(SERVICE_CONFIGURATION_ELEMENT_NAME);
configMemento.putString(ATTR_ID, configurationId);
configMemento.putString(ATTR_NAME, configurationName);
Set<IService> services = config.getServices();
for (IService service : services) {
IServiceProvider provider = config.getServiceProvider(service);
if(provider != null) {
IMemento serviceMemento = configMemento.createChild(SERVICE_ELEMENT_NAME);
serviceMemento.putString(ATTR_ID, service.getId());
serviceMemento.putString(ATTR_PROVIDER_ID, provider.getId());
if (provider instanceof ServiceProvider) {
IMemento providerMemento = serviceMemento.createChild(PROVIDER_CONFIGURATION_ELEMENT_NAME);
((ServiceProvider)provider).saveState(providerMemento);
}
}
}
}
for (Entry<IProject, Map<String, IServiceConfiguration>> entry : projectConfigs.entrySet()) {
IProject project = entry.getKey();
if (!project.exists()) {// Skip over deleted projects
continue;
}
String projectName = project.getName();
IMemento projectMemento = rootMemento.createChild(PROJECT_ELEMENT_NAME);
projectMemento.putString(ATTR_NAME, projectName);
Map<String, IServiceConfiguration> configurations = entry.getValue();
for (IServiceConfiguration config : configurations.values()) {
String configurationId = config.getId();
IMemento configMemento = projectMemento.createChild(SERVICE_CONFIGURATION_ELEMENT_NAME);
configMemento.putString(ATTR_ID, configurationId);
IServiceConfiguration active = activeConfigs.get(project);
configMemento.putBoolean(ATTR_ACTIVE, active != null);
}
}
rootMemento.save(writer);
}
/** Default location to save service model configuration */
private final IPath defaultSaveFile;
private Map<String, IServiceConfiguration> fConfigurations = new HashMap<String, IServiceConfiguration>();
private Map<IProject, Map<String, IServiceConfiguration>> fProjectConfigurations = new HashMap<IProject, Map<String, IServiceConfiguration>>();
private Map<IProject, IServiceConfiguration> fActiveConfigurations = new HashMap<IProject, IServiceConfiguration>();
private Map<IProject, Set<IService>> fProjectServices = new HashMap<IProject, Set<IService>>();
private Map<String, IService> fServices = null;
private Set<IService> fServiceSet = null;
private Map<String, Set<IService>> fNatureServices = null;
private IServiceConfiguration fDefaultServiceConfiguration = null;
private ServiceModelEventManager fEventManager = new ServiceModelEventManager();
private static ServiceModelManager fInstance;
public static synchronized ServiceModelManager getInstance() {
if(fInstance == null) {
fInstance = new ServiceModelManager();
}
return fInstance;
}
private ServiceModelManager() {
defaultSaveFile = Activator.getDefault().getStateLocation().append(DEFAULT_SAVE_FILE_NAME);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#addConfiguration(org.eclipse.core.resources.IProject, org.eclipse.ptp.services.core.IServiceConfiguration)
*/
public void addConfiguration(IProject project, IServiceConfiguration conf) {
if(project == null || conf == null)
throw new NullPointerException();
Map<String, IServiceConfiguration> confs = fProjectConfigurations.get(project);
if(confs == null) {
confs = new HashMap<String, IServiceConfiguration>();
fProjectConfigurations.put(project, confs);
fActiveConfigurations.put(project, conf);
}
confs.put(conf.getId(), conf);
Set<IService> services = fProjectServices.get(project);
if(services == null) {
services = new HashSet<IService>();
fProjectServices.put(project, services);
}
for(IServiceConfiguration config : confs.values()) {
for(IService service : config.getServices()) {
services.add(service);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#addEventListener(org.eclipse.ptp.services.core.IServiceModelEventListener, int)
*/
public void addEventListener(IServiceModelEventListener listener, int type) {
fEventManager.addEventListener(listener, type);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getActiveConfiguration(org.eclipse.core.resources.IProject)
*/
public IServiceConfiguration getActiveConfiguration(IProject project) {
return getConf(fActiveConfigurations, project);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getConfiguration(org.eclipse.core.resources.IProject, java.lang.String)
*/
public IServiceConfiguration getConfiguration(IProject project, String name) {
Map<String, IServiceConfiguration> confMap = getConf(fProjectConfigurations, project);
for (IServiceConfiguration conf : confMap.values()) {
if (conf.getName().equals(name)) {
return conf;
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getConfiguration(java.lang.String)
*/
public IServiceConfiguration getConfiguration(String id) {
return fConfigurations.get(id);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getConfigurations()
*/
public Set<IServiceConfiguration> getConfigurations() {
return new HashSet<IServiceConfiguration>(fConfigurations.values());
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getConfigurations(org.eclipse.core.resources.IProject)
*/
public Set<IServiceConfiguration> getConfigurations(IProject project) {
return new HashSet<IServiceConfiguration>(getConf(fProjectConfigurations, project).values());
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getDefaultConfiguration()
*/
public IServiceConfiguration getActiveConfiguration() {
return fDefaultServiceConfiguration;
}
/**
* Get the set of projects which use the specified service configuration
*
* @param serviceConfiguration The service configuration
* @return Set of projects which use the service configuration
*/
public Set<IProject> getProjectsForConfiguration(IServiceConfiguration serviceConfiguration) {
Set<IProject> projects;
Set<IProject> projectsForConfig;
// Get the set of projects known to the service model manager
projects = fProjectConfigurations.keySet();
projectsForConfig = new HashSet<IProject>();
// For each project, check if it uses the specified service configuration
// If so, add the project to the projectsForConfig set.
for (IProject project : projects) {
Set<IServiceConfiguration> configs;
configs = getConfigurations(project);
for (IServiceConfiguration config : configs) {
if (config == serviceConfiguration) {
projectsForConfig.add(project);
}
}
}
return projectsForConfig;
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getService(java.lang.String)
*/
public IService getService(String id) {
loadServices();
return fServices.get(id);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getServiceProvider(org.eclipse.ptp.services.core.IServiceProviderDescriptor)
*/
public IServiceProvider getServiceProvider(IServiceProviderDescriptor desc) {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(Activator.PLUGIN_ID, PROVIDER_EXTENSION_ID);
if (extensionPoint != null) {
for (IExtension extension : extensionPoint.getExtensions()) {
for (IConfigurationElement element : extension.getConfigurationElements()) {
if (element.getName().equals(PROVIDER_ELEMENT_NAME)) {
if (element.getAttribute(ATTR_ID).equals(desc.getId())) {
try {
IServiceProvider provider = (IServiceProvider) element.createExecutableExtension(ATTR_CLASS);
if (provider instanceof ServiceProvider) {
((ServiceProvider)provider).setDescriptor(desc);
}
return provider;
} catch (Exception e) {
Activator.getDefault().log(e);
return null;
}
}
}
}
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getServices()
*/
public Set<IService> getServices() {
loadServices();
return fServiceSet;
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getServices(org.eclipse.core.resources.IProject)
*/
public Set<IService> getServices(IProject project) {
return getConf(fProjectServices, project);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#getServices(java.lang.String)
*/
public Set<IService> getServices(String natureId) {
loadServices();
return fNatureServices.get(natureId);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#isConfigured(org.eclipse.core.resources.IProject)
*/
public boolean isConfigured(IProject project) {
return fProjectConfigurations.containsKey(project);
}
/**
* Replaces the current service model configuration with what is
* specified in the default save file. If the file does not exist
* then this method does nothing.
*
* This method is not meant to be called outside of the
* <code>org.eclipse.ptp.services.core<code> plugin.
*
* @throws IOException
* @throws CoreException
*/
public void loadModelConfiguration() throws IOException, CoreException {
loadServices();
File file = defaultSaveFile.toFile();
if(file.exists()) {
Reader reader = new BufferedReader(new FileReader(file));
try {
loadModelConfiguration(reader);
} finally {
reader.close();
}
}
notifyListeners(new ServiceModelEvent(this, IServiceModelEvent.SERVICE_MODEL_LOADED));
}
/**
* Replaces the current service model configuration with what is
* specified in the given <code>file</code>.
*
* This method is not meant to be called outside of the
* <code>org.eclipse.ptp.services.core<code> plugin.
*
* @throws IOException
*/
public void loadModelConfiguration(Reader reader) throws IOException, CoreException {
initialize(); // Clear out the existing model
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
XMLMemento rootMemento = XMLMemento.createReadRoot(reader);
for (IMemento configMemento : rootMemento.getChildren(SERVICE_CONFIGURATION_ELEMENT_NAME)) {
String configId = configMemento.getString(ATTR_ID);
String configName = configMemento.getString(ATTR_NAME);
IServiceConfiguration config = newServiceConfiguration(configId, configName);
for (IMemento serviceMemento : configMemento.getChildren(SERVICE_ELEMENT_NAME)) {
String serviceId = serviceMemento.getString(ATTR_ID);
String providerId = serviceMemento.getString(ATTR_PROVIDER_ID);
IService service = getService(serviceId);
if (service != null) {
IServiceProviderDescriptor descriptor = service.getProviderDescriptor(providerId);
if (descriptor != null) {
IServiceProvider provider = getServiceProvider(descriptor);
if (provider != null) {
if (provider instanceof ServiceProvider) {
IMemento providerMemento = serviceMemento.getChild(PROVIDER_CONFIGURATION_ELEMENT_NAME);
((ServiceProvider)provider).restoreState(providerMemento);
}
config.setServiceProvider(service, provider);
} else {
Activator.getDefault().logErrorMessage(Messages.ServiceModelManager_2);
}
} else {
Activator.getDefault().logErrorMessage(Messages.ServiceModelManager_0 + providerId);
}
} else {
Activator.getDefault().logErrorMessage(Messages.ServiceModelManager_1 + serviceId);
}
}
fConfigurations.put(configId, config);
}
for (IMemento projectMemento : rootMemento.getChildren(PROJECT_ELEMENT_NAME)) {
String projectName = projectMemento.getString(ATTR_NAME);
IProject project = root.getProject(projectName);
if (!project.exists()) {
continue;
}
for (IMemento configMemento : projectMemento.getChildren(SERVICE_CONFIGURATION_ELEMENT_NAME)) {
String configId = configMemento.getString(ATTR_ID);
IServiceConfiguration config = fConfigurations.get(configId);
if (config != null) {
addConfiguration(project, config);
Boolean active = configMemento.getBoolean(ATTR_ACTIVE);
if (active != null && active.booleanValue()) {
setActiveConfiguration(project, config);
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#newServiceConfiguration(java.lang.String)
*/
public IServiceConfiguration newServiceConfiguration(String name) {
return newServiceConfiguration(UUID.randomUUID().toString(), name);
}
/**
* Notify listeners of an event occurrence.
*
* This method is not meant to be called outside of the
* <code>org.eclipse.ptp.services.core<code> plugin.
*
* @param event event to notify
*/
public void notifyListeners(IServiceModelEvent event) {
fEventManager.notifyListeners(event);
}
/**
* Prints the current service model to the console, for debugging purposes.
*/
public void printServiceModel() {
System.out.println("Service Model: "); //$NON-NLS-1$
if(fProjectConfigurations.isEmpty())
System.out.println(" Service Model is empty"); //$NON-NLS-1$
for(Entry<IProject, Map<String, IServiceConfiguration>> entry : fProjectConfigurations.entrySet()) {
IProject project = entry.getKey();
System.out.println(" Project: " + project.getName()); //$NON-NLS-1$
for(IServiceConfiguration conf : entry.getValue().values()) {
System.out.println(" " + conf); //$NON-NLS-1$
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#remove(org.eclipse.core.resources.IProject)
*/
public void remove(IProject project) {
if(project == null) {
throw new NullPointerException();
}
fProjectConfigurations.remove(project);
fActiveConfigurations.remove(project);
fProjectServices.remove(project);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#remove(org.eclipse.ptp.services.core.IServiceConfiguration)
*/
public void remove(IServiceConfiguration conf) {
for (IProject project : fProjectConfigurations.keySet()) {
removeConfiguration(project, conf);
if (conf.equals(getActiveConfiguration(project))) {
fActiveConfigurations.remove(project);
}
}
fConfigurations.remove(conf.getId());
notifyListeners(new ServiceModelEvent(conf, IServiceModelEvent.SERVICE_CONFIGURATION_REMOVED));
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#removeConfiguration(org.eclipse.core.resources.IProject, org.eclipse.ptp.services.core.IServiceConfiguration)
*/
public void removeConfiguration(IProject project, IServiceConfiguration conf) {
Map<String, IServiceConfiguration> confs = getConf(fProjectConfigurations, project);
if(confs != null) {
confs.remove(conf.getId());
}
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#removeEventListener(org.eclipse.ptp.services.core.IServiceModelEventListener)
*/
public void removeEventListener(IServiceModelEventListener listener) {
fEventManager.removeEventListener(listener);
}
/**
* Saves the model configuration into the plugin's metadata area using
* the default file name.
* Will not save data for projects that do not exist.
*
* This method is not meant to be called outside of the
* <code>org.eclipse.ptp.services.core<code> plugin.
*
* @throws IOException
*/
public void saveModelConfiguration() throws IOException {
File file = defaultSaveFile.toFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
try {
saveModelConfiguration(writer);
} finally {
writer.close();
}
}
/**
* Saves the service model configuration to the given <code>file</code>.
* Will not save data for projects that do not exist.
*
* This method is not meant to be called outside of the
* <code>org.eclipse.ptp.services.core<code> plugin.
*
* @param file
* @throws IOException
* @throws NullPointerException if file is null
*/
public void saveModelConfiguration(Writer writer) throws IOException {
if(writer == null)
throw new NullPointerException();
saveModelConfiguration(fConfigurations, fProjectConfigurations, fActiveConfigurations, writer);
notifyListeners(new ServiceModelEvent(this, IServiceModelEvent.SERVICE_MODEL_SAVED));
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#setActiveConfiguration(org.eclipse.core.resources.IProject, org.eclipse.ptp.services.core.IServiceConfiguration)
*/
public void setActiveConfiguration(IProject project, IServiceConfiguration configuration) {
Map<String, IServiceConfiguration> confs = getConf(fProjectConfigurations, project);
if(!confs.containsKey(configuration.getId())) {
throw new IllegalArgumentException();
}
fActiveConfigurations.put(project, configuration);
}
/* (non-Javadoc)
* @see org.eclipse.ptp.services.core.IServiceModelManager#setDefaultConfiguration(org.eclipse.ptp.services.core.IServiceConfiguration)
*/
public void setActiveConfiguration(IServiceConfiguration config) {
fDefaultServiceConfiguration = config;
notifyListeners(new ServiceModelEvent(config, IServiceModelEvent.SERVICE_CONFIGURATION_SELECTED));
}
/**
* Initialize model
*/
private void initialize() {
fActiveConfigurations.clear();
fProjectConfigurations.clear();
fProjectServices.clear();
fConfigurations.clear();
}
/**
* Locate and initialize service extensions.
*/
private void loadServices() {
if (fServices != null) {
return;
}
fServices = new HashMap<String, IService>();
fServiceSet = new HashSet<IService>();
fNatureServices = new HashMap<String, Set<IService>>();
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(Activator.PLUGIN_ID, SERVICE_EXTENSION_ID);
if (extensionPoint != null) {
for (IExtension extension : extensionPoint.getExtensions()) {
for (IConfigurationElement element : extension.getConfigurationElements()) {
if (element.getName().equals(SERVICE_ELEMENT_NAME)) {
String id = element.getAttribute(ATTR_ID);
String name = element.getAttribute(ATTR_NAME);
String priority = element.getAttribute(ATTR_PRIORITY);
IConfigurationElement[] natureConf = element.getChildren(NATURE_ELEMENT_NAME);
Set<String> natures = new HashSet<String>();
if (natureConf != null) {
for (IConfigurationElement nature : natureConf) {
String natureId = nature.getAttribute(ATTR_ID);
if (workspace.getNatureDescriptor(natureId) != null) {
natures.add(natureId);
}
}
}
IService service = new Service(id, name, priority, natures);
fServiceSet.add(service);
fServices.put(id, service);
for (String nature : natures) {
Set<IService> svcs = fNatureServices.get(nature);
if (svcs == null) {
svcs = new HashSet<IService>();
fNatureServices.put(nature, svcs);
}
svcs.add(service);
}
}
}
}
}
extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(Activator.PLUGIN_ID, PROVIDER_EXTENSION_ID);
if (extensionPoint != null) {
for (IExtension extension : extensionPoint.getExtensions()) {
for (IConfigurationElement element : extension.getConfigurationElements()) {
if (element.getName().equals(PROVIDER_ELEMENT_NAME)) {
String id = element.getAttribute(ATTR_ID);
String name = element.getAttribute(ATTR_NAME);
String priority = element.getAttribute(ATTR_PRIORITY);
String serviceId = element.getAttribute(ATTR_SERVICE_ID);
IServiceProviderDescriptor desc = new ServiceProviderDescriptor(id, name, serviceId, priority);
IService service = fServices.get(serviceId);
if (service != null) {
service.addServiceProvider(desc);
} else {
Activator.getDefault().logErrorMessage(
NLS.bind(Messages.Services_invalidServiceId, serviceId));
}
}
}
}
}
}
/**
* Create a service configuration with the specified id and name. Used when
* restoring saved state.
*
* @param id id of service configuration
* @param name name of service configuration
* @return service configuration
*/
private IServiceConfiguration newServiceConfiguration(String id, String name) {
IServiceConfiguration config = new ServiceConfiguration(id, name);
fConfigurations.put(id, config);
notifyListeners(new ServiceModelEvent(config, IServiceModelEvent.SERVICE_CONFIGURATION_ADDED));
return config;
}
}