diff --git a/bundles/org.eclipse.osgi.tests/.gitignore b/bundles/org.eclipse.osgi.tests/.gitignore index 9877a0e1654..25b1753c6ea 100644 --- a/bundles/org.eclipse.osgi.tests/.gitignore +++ b/bundles/org.eclipse.osgi.tests/.gitignore @@ -2,3 +2,4 @@ /bundle_tests/ /inner/ .DS_Store +/OSGI-INF/org.eclipse.osgi.*.xml diff --git a/bundles/org.eclipse.osgi.tests/.settings/org.eclipse.pde.ds.annotations.prefs b/bundles/org.eclipse.osgi.tests/.settings/org.eclipse.pde.ds.annotations.prefs new file mode 100644 index 00000000000..73a356b6d05 --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/.settings/org.eclipse.pde.ds.annotations.prefs @@ -0,0 +1,8 @@ +classpath=true +dsVersion=V1_3 +eclipse.preferences.version=1 +enabled=true +generateBundleActivationPolicyLazy=true +path=OSGI-INF +validationErrorLevel=error +validationErrorLevel.missingImplicitUnbindMethod=error diff --git a/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF index 38d394ae348..8ad8eb2c56b 100644 --- a/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF @@ -17,3 +17,10 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 DynamicImport-Package: ext.framework.b Eclipse-BundleShape: dir Automatic-Module-Name: org.eclipse.osgi.tests +Service-Component: OSGI-INF/org.eclipse.osgi.tests.services.resolver.LazyServiceComponentActivationDeadLockTest$MavenImpl.xml, + OSGI-INF/org.eclipse.osgi.tests.services.resolver.LazyServiceComponentActivationDeadLockTest$MavenModelManager$HeavyComponent.xml, + OSGI-INF/org.eclipse.osgi.tests.services.resolver.LazyServiceComponentActivationDeadLockTest$MavenModelManager.xml, + OSGI-INF/org.eclipse.osgi.tests.services.resolver.LazyServiceComponentActivationDeadLockTest$MavenProjectManager$HeavyComponent.xml, + OSGI-INF/org.eclipse.osgi.tests.services.resolver.LazyServiceComponentActivationDeadLockTest$MavenProjectManager.xml, + OSGI-INF/org.eclipse.osgi.tests.services.resolver.LazyServiceComponentActivationDeadLockTest$ProjectConfigurationManager.xml, + OSGI-INF/org.eclipse.osgi.tests.services.resolver.LazyServiceComponentActivationDeadLockTest$RepositoryRegistry.xml diff --git a/bundles/org.eclipse.osgi.tests/build.properties b/bundles/org.eclipse.osgi.tests/build.properties index 91c0b2d8bdb..b6d6d1b3b0a 100644 --- a/bundles/org.eclipse.osgi.tests/build.properties +++ b/bundles/org.eclipse.osgi.tests/build.properties @@ -19,7 +19,8 @@ bin.includes = .,\ test.xml,\ bundle_tests/*.jar,\ plugin.xml,\ - profile.list + profile.list,\ + OSGI-INF/ source.bundle_tests/test.jar = bundles_src/test/ manifest.bundle_tests/test.jar = META-INF/MANIFEST.MF @@ -416,4 +417,4 @@ jars.compile.order = bundle_tests/ext.framework.b.jar,\ bundle_tests/test.dynamic.privateimport.jar,\ bundle_tests/test.bug490902.b.jar,\ bundle_tests/test.bug490902.a.jar,\ - bundle_tests/mrBundleInputBase.jar \ No newline at end of file + bundle_tests/mrBundleInputBase.jar diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java index 9b9faa5cba7..9c0ae6d4c9e 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java @@ -28,6 +28,7 @@ XFriendsInternalResolverTest.class, // GenericCapabilityTest.class, // OSGiCapabilityTest.class, // - DevModeTest.class }) + DevModeTest.class, // + LazyServiceComponentActivationDeadLockTest.class }) public class AllTests { } diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/LazyServiceComponentActivationDeadLockTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/LazyServiceComponentActivationDeadLockTest.java new file mode 100644 index 00000000000..695a733c949 --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/LazyServiceComponentActivationDeadLockTest.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2022, 2022 Hannes Wellmann and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Hannes Wellmann - initial API and implementation + *******************************************************************************/ + +package org.eclipse.osgi.tests.services.resolver; + +import static org.junit.Assert.assertNotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; + +public class LazyServiceComponentActivationDeadLockTest { + + // TODO: add test cases for dead-lock due to lazy activation in different + // bundles?! + + @Test + public void testLateBindingInSameBundleDeadLock() throws Exception { + BundleContext ctx = FrameworkUtil.getBundle(LazyServiceComponentActivationDeadLockTest.class) + .getBundleContext(); + ServiceReference reference = ctx + .getServiceReference(ProjectConfigurationManager.class); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + try { + Future service = executor.submit(() -> ctx.getService(reference)); + assertNotNull(service.get(5, TimeUnit.SECONDS)); // times out in case of dead-lock + } finally { + executor.shutdown(); +// ctx.ungetService(reference); + } + } + + @Component(service = ProjectConfigurationManager.class) + public static class ProjectConfigurationManager { + + @Reference + MavenImpl maven; + + @Reference + MavenModelManager mavenModelManager; + + } + + @Component(service = MavenModelManager.class) + public static class MavenModelManager { + + @Reference + private MavenProjectManager projectManager; + + // The nested component below ensures that the activation of this component + // starts after the activation of MavenProjectManager has started in the SCR + // Actor thread. + + @Reference + HeavyComponent AAA; // Capital letters are important to ensure this reference is handled first + + @Component(service = HeavyComponent.class) + public static class HeavyComponent { + + @Activate + public void activated() throws InterruptedException { + MavenProjectManager.HeavyComponent.ACTIVATED.await(); + } + } + } + + @Component(service = MavenProjectManager.class) + public static class MavenProjectManager { + + private final List listenerManager = new ArrayList<>(); + + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + public void addMavenProjectChangedListener(ProjectConfigurationManager listener) { + listenerManager.add(listener); + } + + public void removeMavenProjectChangedListener(ProjectConfigurationManager listener) { + listenerManager.remove(listener); + } + + // The nested component below ensures that the activation of this component + // starts after the activation of MavenModelManager has started in the main + // thread. + + @Reference + HeavyComponent AAA; // Capital letters are important to ensure this reference is handled first + + @Component(service = HeavyComponent.class) + public static class HeavyComponent { + public static final CountDownLatch ACTIVATED = new CountDownLatch(1); + + @Activate + public void activated() { + ACTIVATED.countDown(); + } + } + } + + @Component(service = MavenImpl.class) + public static class MavenImpl { + + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + private final List settingsListeners = new CopyOnWriteArrayList<>(); + } + + @Component(service = RepositoryRegistry.class) + public static class RepositoryRegistry { + + @Reference + private MavenImpl maven; + + @Reference + MavenProjectManager projectManager; + } + +}