From ca1b1c362560664c03ee81d852e5724d6fe4b363 Mon Sep 17 00:00:00 2001 From: Juergen Albert Date: Wed, 22 Jan 2025 14:14:43 +0100 Subject: [PATCH] OSGi Service as Global EPackageRegistry The default EPackage Registry can be overwritten in mutlple ways (System property or ExtensionPoint). With this, if non of the other mechanisms apply, there now can be a service that acts as the static Registry. Signed-off-by: Juergen Albert --- .../src/org/eclipse/emf/common/EMFPlugin.java | 3 + .../META-INF/MANIFEST.MF | 2 +- plugins/org.eclipse.emf.ecore/pom.xml | 2 +- .../eclipse/emf/ecore/plugin/EcorePlugin.java | 500 +++++++++++++++++- .../Test EMF Core.launch | 2 +- .../org/eclipse/emf/test/core/AllSuites.java | 3 +- .../ServiceBasedEPackageRegistryTest.java | 213 ++++++++ 7 files changed, 718 insertions(+), 7 deletions(-) create mode 100644 tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/ServiceBasedEPackageRegistryTest.java diff --git a/plugins/org.eclipse.emf.common/src/org/eclipse/emf/common/EMFPlugin.java b/plugins/org.eclipse.emf.common/src/org/eclipse/emf/common/EMFPlugin.java index 1fad5d4e08..d513d16cb5 100644 --- a/plugins/org.eclipse.emf.common/src/org/eclipse/emf/common/EMFPlugin.java +++ b/plugins/org.eclipse.emf.common/src/org/eclipse/emf/common/EMFPlugin.java @@ -82,6 +82,9 @@ public abstract class EMFPlugin extends DelegatingResourceLocator implements Res boolean result = false; try { + // With this we make sure, that we are really in a proper Eclipse Environment with everything we need. + // Some supplements pretend that the Platform is running, but don't provide a ExtensionRegistry. + FrameworkUtil.getBundle(EMFPlugin.class).loadClass("org.eclipse.core.runtime.IRegistryChangeListener"); result = Platform.isRunning(); } catch (Throwable exception) diff --git a/plugins/org.eclipse.emf.ecore/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.ecore/META-INF/MANIFEST.MF index 23079f6048..e87ce86c9a 100644 --- a/plugins/org.eclipse.emf.ecore/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.ecore/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.emf.ecore;singleton:=true -Bundle-Version: 2.38.0.qualifier +Bundle-Version: 2.39.0.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.emf.ecore.plugin.EcorePlugin$Implementation$Activator Bundle-Vendor: %providerName diff --git a/plugins/org.eclipse.emf.ecore/pom.xml b/plugins/org.eclipse.emf.ecore/pom.xml index bf375057bf..7fdd436ff3 100644 --- a/plugins/org.eclipse.emf.ecore/pom.xml +++ b/plugins/org.eclipse.emf.ecore/pom.xml @@ -12,7 +12,7 @@ org.eclipse.emf org.eclipse.emf.ecore - 2.38.0-SNAPSHOT + 2.39.0-SNAPSHOT eclipse-plugin diff --git a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/plugin/EcorePlugin.java b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/plugin/EcorePlugin.java index 2b5d3ba9e2..edfb873bbb 100644 --- a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/plugin/EcorePlugin.java +++ b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/plugin/EcorePlugin.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -27,6 +29,8 @@ import java.util.Map; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.regex.Matcher; @@ -52,11 +56,19 @@ import org.eclipse.emf.common.util.ResourceLocator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.common.util.WrappedException; +import org.eclipse.emf.ecore.EFactory; import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EPackage.Registry; import org.eclipse.emf.ecore.impl.EPackageRegistryImpl; import org.eclipse.emf.ecore.resource.URIConverter; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; /** @@ -69,6 +81,11 @@ public class EcorePlugin extends EMFPlugin * The singleton instance of the plugin. */ public static final EcorePlugin INSTANCE = new EcorePlugin(); + + /** + * Feature toggle to enable OSGi ServiceTracker to replace {@link EPackage.Registry} with a registered Service + */ + public static boolean USE_OSGI_SERVICE_AS_EPACKAGE_REGISTRY = Boolean.getBoolean("org.eclipse.emf.ecore.EPackage.Registry.OSGI_SERVICE"); /** * Creates the singleton instance. @@ -586,13 +603,18 @@ public static Map getEPackageNsURIToDynamicModelLocationMap(boolean */ static public class Implementation extends EclipsePlugin { - /** + + + private PlainOSGiBundleActivator plainOSGiBundleActivator; + + /** * Creates the singleton instance. */ public Implementation() { super(); plugin = this; + plainOSGiBundleActivator = new PlainOSGiBundleActivator(); } /** @@ -657,9 +679,61 @@ public void start(BundleContext context) throws Exception { super.start(context); ExtensionProcessor.internalProcessExtensions(); - + if(plainOSGiBundleActivator != null) + { + plainOSGiBundleActivator.start(context); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + if(plainOSGiBundleActivator != null) + { + plainOSGiBundleActivator.stop(context); + } } + /** + * In Case we are not running in Eclipse, this BundleActivator will be used + * @since 2.40 + */ + public static class PlainOSGiBundleActivator implements BundleActivator { + + private ServiceTracker osgiEpackageRegistryTracker = null; + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + @SuppressWarnings("unchecked") + @Override + public void start(BundleContext context) throws Exception { + if(getDefaultRegistryImplementation() != null && getDefaultRegistryImplementation() instanceof OSGiDelegatEPackageRegistry) + { + osgiEpackageRegistryTracker = new ServiceTracker<>(context, OSGiDelegatEPackageRegistry.FILTER, (ServiceTrackerCustomizer) getDefaultRegistryImplementation()); + osgiEpackageRegistryTracker.open(); + } + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + if(osgiEpackageRegistryTracker != null) + { + osgiEpackageRegistryTracker.close(); + } + } + + } + /** * @since 2.10 */ @@ -668,7 +742,14 @@ public static final class Activator extends EMFPlugin.OSGiDelegatingBundleActiva @Override protected BundleActivator createBundle() { - return new Implementation(); + if(IS_ECLIPSE_RUNNING) + { + return new Implementation(); + } else + { + // If we are running in OSGi but not in Eclipse + return new PlainOSGiBundleActivator(); + } } } } @@ -1098,6 +1179,11 @@ public String getSymbolicName() */ public static EPackage.Registry getDefaultRegistryImplementation() { + if(defaultRegistryImplementation == null && EMFPlugin.IS_OSGI_RUNNING && USE_OSGI_SERVICE_AS_EPACKAGE_REGISTRY) + { + OSGiDelegatEPackageRegistry delegatorRegsitry = new OSGiDelegatEPackageRegistry(); + defaultRegistryImplementation = delegatorRegsitry; + } return defaultRegistryImplementation; } @@ -1309,4 +1395,412 @@ private static void computeModels(Map pluginMap, Map nsUR * Since 2.14 */ public static final String ANNOTATION_VALIDATOR_PPID = "annotation_validator"; + + /** + * A {@link Registry} that tries to look for a service as a global Registry. Due + * to the dynamic nature of OSGi Services and the static nature of the default + * {@link Registry} of {@link Registry#INSTANCE} it provides an internal backup. + * Which is used until the service comes around. All write operations will be + * stored with the backup as well, so it can take up its role, when the service + * goes away. When a suitable Service comes around, all statically added + * {@link EPackage}s will be added to the incoming Registry. + * + * Only {@link Registry} Services with the property + * "emf.default.epackage.registry=true" will be considered. If multiple match, + * the one with the highest service rank or if non is present, the first one + * wins. + */ + private static class OSGiDelegatEPackageRegistry implements Registry, ServiceTrackerCustomizer { + + public static Filter FILTER = null; + static { + try { + FILTER = FrameworkUtil.createFilter( + "(&(" + Constants.OBJECTCLASS + "=" + Registry.class.getName() + ")(emf.default.epackage.registry=true))"); + } catch (Exception e) { + // Must not happen + } + } + + private final Registry backup = new EPackageRegistryImpl(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + private final List> knownReferences = new ArrayList<>(); + + private ServiceReference currentDelegateRef = null; + private Registry delegate = null; + + private BundleContext context; + + /** + * Sets the BundleContext to use. Must be set before the Servicetracker is + * opened. + * + * @param context the context to set + */ + public BundleContext getBundleContext() { + if (context == null) { + context = FrameworkUtil.getBundle(getClass()).getBundleContext(); + } + return this.context; + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#size() + */ + @Override + public int size() { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.size(); + } else { + return delegate.size(); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#isEmpty() + */ + @Override + public boolean isEmpty() { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.isEmpty(); + } else { + return delegate.isEmpty(); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#containsKey(java.lang.Object) + */ + @Override + public boolean containsKey(Object key) { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.containsKey(key); + } else { + return delegate.containsKey(key); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#containsValue(java.lang.Object) + */ + @Override + public boolean containsValue(Object value) { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.containsValue(value); + } else { + return delegate.containsValue(value); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#get(java.lang.Object) + */ + @Override + public Object get(Object key) { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.get(key); + } else { + return delegate.get(key); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + @Override + public Object put(String key, Object value) { + lock.readLock().lock(); + try { + if (delegate != null) { + backup.put(key, value); + return delegate.put(key, value); + } + return backup.put(key, value); + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#remove(java.lang.Object) + */ + @Override + public Object remove(Object key) { + lock.readLock().lock(); + try { + if (delegate != null) { + backup.remove(key); + return delegate.remove(key); + } + return backup.remove(key); + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#putAll(java.util.Map) + */ + @Override + public void putAll(Map m) { + lock.readLock().lock(); + try { + if (delegate == null) { + backup.putAll(m); + } else { + delegate.putAll(m); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#clear() + */ + @Override + public void clear() { + lock.readLock().lock(); + try { + if (delegate == null) { + backup.clear(); + } else { + delegate.clear(); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#keySet() + */ + @Override + public Set keySet() { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.keySet(); + } else { + return delegate.keySet(); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#values() + */ + @Override + public Collection values() { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.values(); + } else { + return delegate.values(); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#entrySet() + */ + @Override + public Set> entrySet() { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.entrySet(); + } else { + return delegate.entrySet(); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.ecore.EPackage.Registry#getEPackage(java.lang.String) + */ + @Override + public EPackage getEPackage(String nsURI) { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.getEPackage(nsURI); + } else { + return delegate.getEPackage(nsURI); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.ecore.EPackage.Registry#getEFactory(java.lang.String) + */ + @Override + public EFactory getEFactory(String nsURI) { + lock.readLock().lock(); + try { + if (delegate == null) { + return backup.getEFactory(nsURI); + } else { + return delegate.getEFactory(nsURI); + } + } finally { + lock.readLock().unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi. + * framework.ServiceReference) + */ + @Override + public Object addingService(ServiceReference reference) { + knownReferences.add(reference); + handleDelegateRegistryChange(); + return new Object(); + } + + /* + * (non-Javadoc) + * + * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(org.osgi. + * framework.ServiceReference, java.lang.Object) + */ + @Override + public void modifiedService(ServiceReference reference, Object service) { + handleDelegateRegistryChange(); + } + + /* + * (non-Javadoc) + * + * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi. + * framework.ServiceReference, java.lang.Object) + */ + @Override + public void removedService(ServiceReference reference, Object service) { + knownReferences.remove(reference); + handleDelegateRegistryChange(); + + } + + private void handleDelegateRegistryChange() { + Collections.sort(knownReferences, + Comparator.comparingInt(this::getServiceRank).thenComparing(Comparator.reverseOrder())); + Collections.reverse(knownReferences); + if (knownReferences.isEmpty() && delegate != null) { + unsetDelegateSafe(); + return; + } + ServiceReference reference = knownReferences.get(0); + if (!reference.equals(currentDelegateRef)) { + handleDelegateUpdate(reference); + } + } + + private void handleDelegateUpdate(ServiceReference newReference) { + Registry newDelegate = getBundleContext().getService(newReference); + if (newDelegate != null) { + lock.writeLock().lock(); + try { + unsetDelegate(); + currentDelegateRef = newReference; + delegate = newDelegate; + newDelegate.putAll(backup); + } finally { + lock.writeLock().unlock(); + } + } + } + + private void unsetDelegateSafe() { + lock.writeLock().lock(); + try { + unsetDelegate(); + } finally { + lock.writeLock().unlock(); + } + } + + private void unsetDelegate() { + if (currentDelegateRef != null) { + getBundleContext().ungetService(currentDelegateRef); + currentDelegateRef = null; + delegate = null; + } + } + + private int getServiceRank(ServiceReference s) { + Object sr = s.getProperty(Constants.SERVICE_RANKING); + if (sr != null && sr instanceof Integer) { + return (Integer) sr; + } else { + return Integer.valueOf(0); + } + } + } } \ No newline at end of file diff --git a/tests/org.eclipse.emf.test.core/Test EMF Core.launch b/tests/org.eclipse.emf.test.core/Test EMF Core.launch index 19b34f28dd..26ff2271a5 100644 --- a/tests/org.eclipse.emf.test.core/Test EMF Core.launch +++ b/tests/org.eclipse.emf.test.core/Test EMF Core.launch @@ -36,7 +36,7 @@ - + diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java index 526489c69a..73a88daf97 100644 --- a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java @@ -69,7 +69,8 @@ org.eclipse.emf.test.core.common.util.PoolTest.class, org.eclipse.emf.test.core.common.util.StringPoolTest.class, org.eclipse.emf.test.core.common.util.SegmentSequenceTest.class, - org.eclipse.emf.test.core.common.util.URITest.class + org.eclipse.emf.test.core.common.util.URITest.class, + org.eclipse.emf.test.core.ecore.ServiceBasedEPackageRegistryTest.class }) public class AllSuites { diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/ServiceBasedEPackageRegistryTest.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/ServiceBasedEPackageRegistryTest.java new file mode 100644 index 0000000000..15f37d97b0 --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/ServiceBasedEPackageRegistryTest.java @@ -0,0 +1,213 @@ +package org.eclipse.emf.test.core.ecore; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EPackage.Registry; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.emf.ecore.impl.EPackageRegistryImpl; +import org.eclipse.emf.ecore.plugin.EcorePlugin; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; + +public class ServiceBasedEPackageRegistryTest { + + private BundleContext context; + private List> registeredServices = new ArrayList<>(); + private EPackage testPackage; + + @Before + public void before() { + if(EcorePlugin.USE_OSGI_SERVICE_AS_EPACKAGE_REGISTRY) + { + context = FrameworkUtil.getBundle(getClass()).getBundleContext(); + createTestPackage(); + } + } + + private void createTestPackage() { + testPackage = EcoreFactory.eINSTANCE.createEPackage(); + testPackage.setNsURI("http://test.de/test/1.0"); + } + + @After + public void after() { + if(EcorePlugin.USE_OSGI_SERVICE_AS_EPACKAGE_REGISTRY) + { + EPackage.Registry.INSTANCE.remove(testPackage.getNsURI()); + if (!registeredServices.isEmpty()) + { + registeredServices.forEach(ServiceRegistration::unregister); + registeredServices.clear(); + ; + } + } + } + + /* + * Tests that the Registered Registry is picked up and that changes to the + * static Registry is picked up. + */ + @Test + public void testBasicRegistration() { + if(EcorePlugin.USE_OSGI_SERVICE_AS_EPACKAGE_REGISTRY) + { + Registry staticRegistry = EPackage.Registry.INSTANCE; + + assertNotNull(staticRegistry); + + EPackage manualPackage = staticRegistry.getEPackage(testPackage.getNsURI()); + assertNull(manualPackage); + + staticRegistry.put(testPackage.getNsURI(), testPackage); + + manualPackage = staticRegistry.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + + EPackageRegistryImpl registryService = new EPackageRegistryImpl(); + + ServiceRegistration registryServiceRegistration = context.registerService(Registry.class, registryService, + FrameworkUtil.asDictionary(Collections.singletonMap("emf.default.epackage.registry", true))); + + registeredServices.add(registryServiceRegistration); + + manualPackage = registryService.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + + staticRegistry.remove(testPackage.getNsURI()); + manualPackage = registryService.getEPackage(testPackage.getNsURI()); + assertNull(manualPackage); + + staticRegistry.put(testPackage.getNsURI(), testPackage); + registryServiceRegistration.unregister(); + registeredServices.remove(registryServiceRegistration); + + manualPackage = staticRegistry.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + } + } + + /* + * The expectation is that a second Registry will be ignored + */ + @Test + public void testDoubleRegistration() { + if(EcorePlugin.USE_OSGI_SERVICE_AS_EPACKAGE_REGISTRY) + { + Registry staticRegistry = EPackage.Registry.INSTANCE; + + assertNotNull(staticRegistry); + + EPackage manualPackage = staticRegistry.getEPackage(testPackage.getNsURI()); + assertNull(manualPackage); + + staticRegistry.put(testPackage.getNsURI(), testPackage); + + manualPackage = staticRegistry.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + + EPackageRegistryImpl mainRegistry = new EPackageRegistryImpl(); + + ServiceRegistration registration1 = context.registerService(Registry.class, mainRegistry, + FrameworkUtil.asDictionary(Collections.singletonMap("emf.default.epackage.registry", true))); + registeredServices.add(registration1); + + manualPackage = mainRegistry.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + + EPackageRegistryImpl toIgnoreRegistry = new EPackageRegistryImpl(); + + ServiceRegistration registration2 = context.registerService(Registry.class, mainRegistry, + FrameworkUtil.asDictionary(Collections.singletonMap("emf.default.epackage.registry", true))); + registeredServices.add(registration2); + // we have to use something random in order to avoid interference between tests, + // because of the static nature of the registry + String random = UUID.randomUUID().toString(); + + // just to make sure nothing lands there + staticRegistry.put(random, testPackage); + + manualPackage = toIgnoreRegistry.getEPackage(random); + assertNull(manualPackage); + } + } + + /* + * The expectation is that a second Registry will replace the old registry, due + * to the higher service rank + */ + @Test + public void testDoubleRegistrationWithServiceRank() { + if(EcorePlugin.USE_OSGI_SERVICE_AS_EPACKAGE_REGISTRY) + { + Registry staticRegistry = EPackage.Registry.INSTANCE; + + assertNotNull(staticRegistry); + + EPackage manualPackage = staticRegistry.getEPackage(testPackage.getNsURI()); + assertNull(manualPackage); + + staticRegistry.put(testPackage.getNsURI(), testPackage); + + manualPackage = staticRegistry.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + + EPackageRegistryImpl mainRegistry = new EPackageRegistryImpl(); + + ServiceRegistration registration1 = context.registerService(Registry.class, mainRegistry, + FrameworkUtil.asDictionary(Collections.singletonMap("emf.default.epackage.registry", true))); + + registeredServices.add(registration1); + + manualPackage = mainRegistry.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + + EPackageRegistryImpl secondRegistry = new EPackageRegistryImpl(); + + Map props = new HashMap<>(); + props.put("emf.default.epackage.registry", true); + props.put(Constants.SERVICE_RANKING, 100); + ServiceRegistration registration2 = context.registerService(Registry.class, secondRegistry, + FrameworkUtil.asDictionary(props)); + registeredServices.add(registration2); + + manualPackage = secondRegistry.getEPackage(testPackage.getNsURI()); + assertNotNull(manualPackage); + + // we have to use something random in order to avoid interference between tests, + // because of the static nature of the registry + String random = UUID.randomUUID().toString(); + + staticRegistry.put(random, testPackage); + + manualPackage = secondRegistry.getEPackage(random); + assertNotNull(manualPackage); + + // Nothing must be added to the old registry + manualPackage = mainRegistry.getEPackage(random); + assertNull(manualPackage); + + // now we unregister the higher ranked registry and the test2 must be part of + // the old one, as it becomes active again + registration2.unregister(); + registeredServices.remove(registration2); + + manualPackage = mainRegistry.getEPackage(random); + assertNotNull(manualPackage); + } + } + +} \ No newline at end of file