From b6ec1fc74e3e998d1ba30ec281e1ff4f34c6860f Mon Sep 17 00:00:00 2001 From: Dirk Fauth Date: Tue, 14 Mar 2017 20:32:10 +0100 Subject: Bug 413287 - [e4] EclipseContextOSGi does not deal with OSGi-Services appropriately Change-Id: Id5ae889ffc78f65d2c943e3941a3c488227838bf Signed-off-by: Dirk Fauth --- .../internal/contexts/osgi/EclipseContextOSGi.java | 50 ++++----- .../org.eclipse.e4.core.tests/META-INF/MANIFEST.MF | 5 +- ...internal.tests.contexts.inject.TestServiceA.xml | 8 ++ ...internal.tests.contexts.inject.TestServiceB.xml | 9 ++ ...tests.contexts.inject.TestServiceController.xml | 7 ++ .../tests/contexts/inject/ServiceContextTest.java | 113 ++++++++++++++++++++- .../tests/contexts/inject/TestOtherService.java | 5 + .../tests/contexts/inject/TestService.java | 5 + .../tests/contexts/inject/TestServiceA.java | 8 ++ .../tests/contexts/inject/TestServiceB.java | 8 ++ .../contexts/inject/TestServiceController.java | 32 ++++++ 11 files changed, 224 insertions(+), 26 deletions(-) create mode 100644 tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceA.xml create mode 100644 tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceB.xml create mode 100644 tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceController.xml create mode 100644 tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestOtherService.java create mode 100644 tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestService.java create mode 100644 tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceA.java create mode 100644 tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceB.java create mode 100644 tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceController.java diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/osgi/EclipseContextOSGi.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/osgi/EclipseContextOSGi.java index 1167d7c3c..149227101 100644 --- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/osgi/EclipseContextOSGi.java +++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/osgi/EclipseContextOSGi.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2015 IBM Corporation and others. + * Copyright (c) 2011, 2017 IBM Corporation 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 @@ -87,13 +87,6 @@ public class EclipseContextOSGi extends EclipseContext implements ServiceListene return service; } - /** - * Returns the service name for a service reference - */ - private String serviceName(ServiceReference reference) { - return ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0]; - } - @Override public void dispose() { for (ServiceReference ref : refs.values()) { @@ -109,21 +102,29 @@ public class EclipseContextOSGi extends EclipseContext implements ServiceListene @Override public void serviceChanged(ServiceEvent event) { ServiceReference ref = event.getServiceReference(); - String name = serviceName(ref); - if (IContextFunction.SERVICE_NAME.equals(name)) // those keep associated names - name = (String) ref.getProperty(IContextFunction.SERVICE_CONTEXT_KEY); + String[] names = ((String[]) ref.getProperty(Constants.OBJECTCLASS)); + for (String name : names) { + if (IContextFunction.SERVICE_NAME.equals(name)) { + name = (String) ref.getProperty(IContextFunction.SERVICE_CONTEXT_KEY); + } + + if (refs.containsKey(name)) { + // retrieve the highest ranked service of the same type + ref = bundleContext.getServiceReference(name); - if (refs.containsKey(name)) { - ServiceReference oldRef = refs.get(name); - if (oldRef != null) - bundleContext.ungetService(oldRef); - if (event.getType() == ServiceEvent.UNREGISTERING) { - refs.put(name, null); - remove(name); - } else { - Object service = bundleContext.getService(ref); - refs.put(name, ref); - set(name, service); + ServiceReference oldRef = refs.get(name); + if (oldRef != null && oldRef != ref) { + bundleContext.ungetService(oldRef); + } + + if (ref != null) { + Object service = bundleContext.getService(ref); + refs.put(name, ref); + set(name, service); + } else { + refs.put(name, null); + remove(name); + } } } } @@ -131,8 +132,9 @@ public class EclipseContextOSGi extends EclipseContext implements ServiceListene @Override public void bundleChanged(BundleEvent event) { // In case OSGi context has not being properly disposed by the application, - // OSGi framework shutdown will trigged uninjection of all consumed OSGi - // service. To avoid this, we detect framework shutdown and release services. + // OSGi framework shutdown will triggered uninjection of all consumed + // OSGi service. To avoid this, we detect framework shutdown and release + // services. if (event.getType() != BundleEvent.STOPPING) return; if (event.getBundle().getBundleId() == 0) diff --git a/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF index 40f7fc430..3dafa7eb6 100644 --- a/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF @@ -38,7 +38,10 @@ Service-Component: OSGI-INF/adder.xml, OSGI-INF/org.eclipse.e4.core.internal.tests.di.extensions.SampleServiceB.xml, OSGI-INF/org.eclipse.e4.core.internal.tests.di.extensions.ComponentEnabler.xml, OSGI-INF/DisabledServiceA.xml, - OSGI-INF/DisabledServiceB.xml + OSGI-INF/DisabledServiceB.xml, + OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceController.xml, + OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceA.xml, + OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceB.xml Eclipse-BundleShape: dir Require-Capability: osgi.extender; filter:="(&(osgi.extender=osgi.component)(version>=1.3)(!(version>=2.0)))", diff --git a/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceA.xml b/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceA.xml new file mode 100644 index 000000000..7cf23f5e6 --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceA.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceB.xml b/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceB.xml new file mode 100644 index 000000000..bed59849e --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceB.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceController.xml b/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceController.xml new file mode 100644 index 000000000..f5a8db8ba --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/OSGI-INF/org.eclipse.e4.core.internal.tests.contexts.inject.TestServiceController.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/ServiceContextTest.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/ServiceContextTest.java index 98f655efd..06e374041 100644 --- a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/ServiceContextTest.java +++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/ServiceContextTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2015 IBM Corporation and others. + * Copyright (c) 2009, 2017 IBM Corporation 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 @@ -15,6 +15,7 @@ package org.eclipse.e4.core.internal.tests.contexts.inject; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.util.ArrayList; @@ -34,6 +35,7 @@ import org.junit.Before; import org.junit.Test; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; @@ -88,6 +90,16 @@ public class ServiceContextTest { } } + static class TestBean { + @Inject + @Optional + TestService testService; + + @Inject + @Optional + TestOtherService testOtherService; + } + private IEclipseContext context; private final List> registrations = new ArrayList<>(); @@ -277,4 +289,103 @@ public class ServiceContextTest { crayon.draw(); } + @Test + public void testOptionalReferences() throws InterruptedException { + BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext(); + IEclipseContext serviceContext = EclipseContextFactory.getServiceContext(context); + TestBean bean = ContextInjectionFactory.make(TestBean.class, serviceContext); + + assertNull(bean.testService); + + ServiceReference ref = context.getServiceReference(TestServiceController.class); + TestServiceController controller = context.getService(ref); + try { + controller.enableTestServiceA(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNotNull(bean.testService); + assertSame(TestServiceA.class, bean.testService.getClass()); + assertNotNull(bean.testOtherService); + assertSame(TestServiceA.class, bean.testOtherService.getClass()); + + controller.enableTestServiceB(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNotNull(bean.testService); + assertSame(TestServiceB.class, bean.testService.getClass()); + assertNotNull(bean.testOtherService); + assertSame(TestServiceB.class, bean.testOtherService.getClass()); + + controller.disableTestServiceB(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNotNull(bean.testService); + assertSame(TestServiceA.class, bean.testService.getClass()); + assertNotNull(bean.testOtherService); + assertSame(TestServiceA.class, bean.testOtherService.getClass()); + + controller.disableTestServiceA(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNull(bean.testService); + assertNull(bean.testOtherService); + } finally { + controller.disableTestServiceA(); + controller.disableTestServiceB(); + // give the service registry and the injection some time to ensure + // clear state after this test + Thread.sleep(100); + } + } + + @Test + public void testServiceRanking() throws InterruptedException { + BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext(); + IEclipseContext serviceContext = EclipseContextFactory.getServiceContext(context); + TestBean bean = ContextInjectionFactory.make(TestBean.class, serviceContext); + + assertNull(bean.testService); + + ServiceReference ref = context.getServiceReference(TestServiceController.class); + TestServiceController controller = context.getService(ref); + try { + controller.enableTestServiceB(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNotNull(bean.testService); + assertSame(TestServiceB.class, bean.testService.getClass()); + assertNotNull(bean.testOtherService); + assertSame(TestServiceB.class, bean.testOtherService.getClass()); + + // enable a service with a lower ranking + controller.enableTestServiceA(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNotNull(bean.testService); + // we expect that still the highest ranked service is injected + assertSame(TestServiceB.class, bean.testService.getClass()); + assertNotNull(bean.testOtherService); + assertSame(TestServiceB.class, bean.testOtherService.getClass()); + + controller.disableTestServiceB(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNotNull(bean.testService); + assertSame(TestServiceA.class, bean.testService.getClass()); + assertNotNull(bean.testOtherService); + assertSame(TestServiceA.class, bean.testOtherService.getClass()); + + controller.disableTestServiceA(); + // give the service registry and the injection some time + Thread.sleep(100); + assertNull(bean.testService); + assertNull(bean.testOtherService); + } finally { + controller.disableTestServiceA(); + controller.disableTestServiceB(); + // give the service registry and the injection some time to ensure + // clear state after this test + Thread.sleep(100); + } + } } diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestOtherService.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestOtherService.java new file mode 100644 index 000000000..b983f3084 --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestOtherService.java @@ -0,0 +1,5 @@ +package org.eclipse.e4.core.internal.tests.contexts.inject; + +public interface TestOtherService { + +} diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestService.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestService.java new file mode 100644 index 000000000..11f52fabd --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestService.java @@ -0,0 +1,5 @@ +package org.eclipse.e4.core.internal.tests.contexts.inject; + +public interface TestService { + +} diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceA.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceA.java new file mode 100644 index 000000000..aa1eac4f0 --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceA.java @@ -0,0 +1,8 @@ +package org.eclipse.e4.core.internal.tests.contexts.inject; + +import org.osgi.service.component.annotations.Component; + +@Component(enabled = false) +public class TestServiceA implements TestService, TestOtherService { + +} diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceB.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceB.java new file mode 100644 index 000000000..32df30315 --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceB.java @@ -0,0 +1,8 @@ +package org.eclipse.e4.core.internal.tests.contexts.inject; + +import org.osgi.service.component.annotations.Component; + +@Component(enabled = false, property = "service.ranking:Integer=5") +public class TestServiceB implements TestService, TestOtherService { + +} diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceController.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceController.java new file mode 100644 index 000000000..6873ed636 --- /dev/null +++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/TestServiceController.java @@ -0,0 +1,32 @@ +package org.eclipse.e4.core.internal.tests.contexts.inject; + +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; + +@Component(service = TestServiceController.class) +public class TestServiceController { + + ComponentContext context; + + @Activate + void activate(ComponentContext context) { + this.context = context; + } + + public void enableTestServiceA() { + this.context.enableComponent(TestServiceA.class.getName()); + } + + public void disableTestServiceA() { + this.context.disableComponent(TestServiceA.class.getName()); + } + + public void enableTestServiceB() { + this.context.enableComponent(TestServiceB.class.getName()); + } + + public void disableTestServiceB() { + this.context.disableComponent(TestServiceB.class.getName()); + } +} -- cgit v1.2.3