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 1fad5d4e0..d513d16cb 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 23079f604..e87ce86c9 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 bf375057b..7fdd436ff 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 2b5d3ba9e..edfb873bb 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 19b34f28d..26ff2271a 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 526489c69..73a88daf9 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 000000000..15f37d97b --- /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