/******************************************************************************* * Copyright (c) 2008, 2014 Stuart McCulloch * 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: * Stuart McCulloch - initial API and implementation *******************************************************************************/ package org.eclipse.sisu.peaberry.eclipse; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.RegistryFactory; import org.eclipse.sisu.peaberry.AttributeFilter; import org.eclipse.sisu.peaberry.Export; import org.eclipse.sisu.peaberry.Import; import org.eclipse.sisu.peaberry.ServiceRegistry; import org.eclipse.sisu.peaberry.ServiceWatcher; import org.eclipse.sisu.peaberry.cache.FilteredServiceWatcher; /** * Eclipse Extension {@link ServiceRegistry} implementation. * * @author mcculls@gmail.com (Stuart McCulloch) */ public final class EclipseRegistry implements ServiceRegistry { private static class SingletonHolder { static final ServiceRegistry thisRegistry = new EclipseRegistry(); } /** * Create a singleton {@link ServiceRegistry} that queries the default * {@link IExtensionRegistry} for named extension point contributions. * * @return Eclipse Extension service registry */ public static ServiceRegistry eclipseRegistry() { return SingletonHolder.thisRegistry; } private final IExtensionRegistry extensionRegistry; // per-class map of extension listeners (much faster than polling) private final ConcurrentMap<String, ExtensionListener> listenerMap = new ConcurrentHashMap<String, ExtensionListener>(16, 0.75f, 2); EclipseRegistry() { extensionRegistry = RegistryFactory.getRegistry(); } public <T> Iterable<Import<T>> lookup(final Class<T> clazz, final AttributeFilter filter) { return new IterableExtension<T>(registerListener(clazz), filter); } public <T> Export<T> add(final Import<T> service) { // Extension registry is currently read-only throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") public <T> void watch(final Class<T> clazz, final AttributeFilter filter, final ServiceWatcher<? super T> watcher) { registerListener(clazz).addWatcher( null == filter ? watcher : new FilteredServiceWatcher(filter, watcher)); } // see bundle activator public void shutdown() { for (final String k : listenerMap.keySet()) { extensionRegistry.removeListener(listenerMap.remove(k)); } } @Override public String toString() { return String.format("EclipseRegistry[%s]", extensionRegistry.toString()); } @Override public int hashCode() { return extensionRegistry.hashCode(); } @Override public boolean equals(final Object rhs) { if (rhs instanceof EclipseRegistry) { return extensionRegistry.equals(((EclipseRegistry) rhs).extensionRegistry); } return false; } private <T> ExtensionListener registerListener(final Class<T> clazz) { final Class<?> safeClazz = null == clazz ? Object.class : clazz; final String clazzName = safeClazz.getName(); ExtensionListener listener; listener = listenerMap.get(clazzName); if (null == listener) { final ExtensionListener newListener = new ExtensionListener(safeClazz); listener = listenerMap.putIfAbsent(clazzName, newListener); if (null == listener) { newListener.start(extensionRegistry); return newListener; } } return listener; } }