/******************************************************************************* * Copyright (c) 2011 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tm.te.runtime.services; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.tm.te.runtime.activator.CoreBundleActivator; import org.eclipse.tm.te.runtime.services.interfaces.IService; /** * Abstract service manager implementation. */ public abstract class AbstractServiceManager<V extends IService> { // map for all services per id private Map<String, List<ServiceProxy>> services = new HashMap<String, List<ServiceProxy>>(); /** * Proxy to provide lazy loading of contributing plug-ins. */ protected class ServiceProxy { private IConfigurationElement configElement = null; public String clazz; private V service = null; private List<Class<? extends V>> serviceTypes = new ArrayList<Class<? extends V>>(); /** * Constructor. */ protected ServiceProxy(IConfigurationElement configElement) { Assert.isNotNull(configElement); this.configElement = configElement; // Read the class attribute. If null, check for the class sub element clazz = configElement.getAttribute("class"); //$NON-NLS-1$ if (clazz == null) { IConfigurationElement[] children = configElement.getChildren("class"); //$NON-NLS-1$ // Single element definition assumed (see extension point schema) if (children.length > 0) { clazz = children[0].getAttribute("class"); //$NON-NLS-1$ } } } /** * Add a type to the proxy. Types are used unless the proxy is instantiated to provide lazy * loading of services. After instantiated, a service will be identified only by its type * and implementing or extending interfaces or super-types. * * @param serviceType The type to add. */ public void addType(Class<? extends V> serviceType) { Assert.isNotNull(serviceType); if (service == null && serviceTypes != null && !serviceTypes.contains(serviceType)) { serviceTypes.add(serviceType); } } /** * Return the real service instance for this proxy. */ @SuppressWarnings("unchecked") protected V getService(boolean unique) { if ((service == null || unique) && configElement != null) { try { // Create the service class instance via the configuration element Object service = configElement.createExecutableExtension("class"); //$NON-NLS-1$ if (service instanceof IService) { if (unique) { return (V) service; } else if (service instanceof IService) { this.service = (V)service; } else { IStatus status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), "Service '" + service.getClass().getName() + "' not of type IService."); //$NON-NLS-1$ //$NON-NLS-2$ Platform.getLog(CoreBundleActivator.getContext().getBundle()) .log(status); } } } catch (CoreException e) { IStatus status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), "Cannot create service '" + clazz + "'.", e); //$NON-NLS-1$ //$NON-NLS-2$ Platform.getLog(CoreBundleActivator.getContext().getBundle()).log(status); } if (serviceTypes != null) { serviceTypes.clear(); } serviceTypes = null; } return service; } /** * Check whether this proxy holds a service that is suitable for the given type. * * @param serviceType The service type * @return */ protected boolean isMatching(Class<? extends V> serviceType) { if (service != null) { return serviceType.isInstance(service); } else if (configElement != null) { if (serviceType.getClass().getName().equals(clazz)) { return true; } for (Class<? extends V> type : serviceTypes) { if (type.equals(serviceType)) { return true; } } } return false; } public boolean equals(V service) { return clazz.equals(service.getClass()); } public boolean equals(ServiceProxy proxy) { return clazz.equals(proxy.clazz); } } /** * Constructor. */ protected AbstractServiceManager() { loadServices(); } /** * @param element * @return */ protected ServiceProxy getServiceProxy(IConfigurationElement element) { return new ServiceProxy(element); } /** * Returns all id's of the registered services. * * @return The list of id's of the registered services. */ public String[] getIds() { return services.keySet().toArray(new String[services.keySet().size()]); } /** * Get a service for the id that implements at least the needed service type. If an interface * type is given, the service with the highest implementation is returned. This may result in a * random selection depending on the extension registration order, especially when a service * interface is implemented two times in different hierarchy paths. If a class type is given, if * available, the service of exactly that class is returned. Otherwise the highest * implementation is returned. * * @param id The id for which a service is needed. * @param serviceType The service type the service should at least implement or extend. * @return The service or <code>null</code>. */ public V getService(String id, Class<? extends V> serviceType) { return getService(id, serviceType, false); } /** * Get a service for the id that implements at least the needed service type. If an interface * type is given, the service with the highest implementation is returned. This may result in a * random selection depending on the extension registration order, especially when a service * interface is implemented two times in different hierarchy paths. If a class type is given, if * available, the service of exactly that class is returned. Otherwise the highest * implementation is returned. * * @param id The id for which a service is needed. * @param serviceType The service type the service should at least implement or extend. * @param unique <code>true</code> if a new instance of the service is needed. * * @return The service or <code>null</code>. */ public V getService(String id, Class<? extends V> serviceType, boolean unique) { Assert.isNotNull(serviceType); if (id == null) { id = ""; //$NON-NLS-1$ } List<ServiceProxy> proxies = services.get(id); if (proxies != null && !proxies.isEmpty()) { List<ServiceProxy> candidates = new ArrayList<ServiceProxy>(); boolean isInterface = serviceType.isInterface(); for (ServiceProxy proxy : proxies) { if (proxy.isMatching(serviceType)) { if (!isInterface && proxy.equals(serviceType)) { V service = proxy.getService(unique); service.setId(id); return service; } candidates.add(proxy); } } V service = null; if (!candidates.isEmpty()) { service = candidates.get(0).getService(unique); service.setId(id); } return service; } return null; } /** * Get a service list for the id that implements at least the needed service type. * * @param id The id for which a service is needed. * @param serviceType The service type the service should at least implement or extend. * @param unique <code>true</code> if a new instance of the service is needed. * @return The service list or empty list. */ public IService[] getServices(String id, Class<? extends V> serviceType, boolean unique) { Assert.isNotNull(serviceType); if (id == null) { id = ""; //$NON-NLS-1$ } List<ServiceProxy> proxies = services.get(id); List<IService> services = new ArrayList<IService>(); if (proxies != null && !proxies.isEmpty()) { List<ServiceProxy> candidates = new ArrayList<ServiceProxy>(); for (ServiceProxy proxy : proxies) { if (proxy.isMatching(serviceType)) { candidates.add(proxy); } } for (ServiceProxy serviceProxy : candidates) { IService service = serviceProxy.getService(unique); service.setId(id); services.add(service); } } return services.toArray(new IService[services.size()]); } /* * Add a service proxy to the list of available services. */ protected boolean addService(String id, ServiceProxy proxy) { Assert.isNotNull(services); Assert.isNotNull(id); Assert.isNotNull(proxy); List<ServiceProxy> proxies = services.get(id); if (proxies == null) { proxies = new ArrayList<ServiceProxy>(); services.put(id, proxies); } Assert.isNotNull(proxies); if (proxies.isEmpty() || !proxies.contains(proxy)) { return proxies.add(proxy); } return false; } /** * Loads the contributed services into proxies (lazy loading!!) and adds them to this manager; */ protected abstract void loadServices(); }