blob: 06e374041941ed9ae47622b3eac8acd2fbe52b84 [file] [log] [blame]
/*******************************************************************************
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 474274
******************************************************************************/
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;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.contexts.RunAndTrack;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.internal.tests.CoreTestsActivator;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.junit.After;
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;
/**
* Tests for contexts used in OSGi services.
*/
public class ServiceContextTest {
class Crayon {
@Inject
IPaletteService palette;
String msg;
public void draw() {
if (palette == null) {
msg = "I'm out of ink!";
} else {
msg = "My ink is " + palette.getColor();
}
}
}
static enum Color {
RED, BLUE, YELLOW, GREEN, ORANGE, PURPLE;
}
interface IPaletteService {
public Color getColor();
}
class PaletteImpl implements IPaletteService {
private final Color color;
PaletteImpl(Color color) {
this.color = color;
}
@Override
public Color getColor() {
return color;
}
}
static class Printer {
@Inject @Optional
PrintService printer;
public void print(String message) {
if (printer != null) {
printer.print(message);
}
}
}
static class TestBean {
@Inject
@Optional
TestService testService;
@Inject
@Optional
TestOtherService testOtherService;
}
private IEclipseContext context;
private final List<ServiceRegistration<?>> registrations = new ArrayList<>();
@Before
public void setUp() throws Exception {
//don't use the global shared service context to avoid contamination across tests
BundleContext bundleContext = CoreTestsActivator.getDefault().getBundleContext();
context = EclipseContextFactory.getServiceContext(bundleContext);
registrations.clear();
}
@After
public void tearDown() throws Exception {
// Consumers must not dispose OSGi context as it is reused
//context.dispose();
for (ServiceRegistration<?> reg : registrations) {
try {
reg.unregister();
} catch (IllegalStateException e) {
//ignore
}
}
registrations.clear();
context = null;
}
@Test
public void testDeclarativeService() {
assertTrue(context.containsKey("sum"));
assertEquals(0, context.get("sum"));
context.set("x", 1);
context.set("y", 2);
int result = (Integer) context.get("sum");
assertEquals("1.0", 3, result);
context.set("x", 5);
result = (Integer) context.get("sum");
assertEquals("1.0", 7, result);
}
/**
* Tests accessing OSGi services through a child context that is not aware of them.
*/
@Test
public void testServiceContextAsParent() {
IEclipseContext child = context.createChild( "child");
DebugOptions service = (DebugOptions) child.get(DebugOptions.class.getName());
assertNotNull(service);
}
@Test
public void testServiceInjection() {
ServiceRegistration<?> reg1 = null;
ServiceRegistration<?> reg2 = null;
try {
Printer userObject = new Printer();
StringPrintService stringPrint1 = new StringPrintService();
BundleContext bundleContext = CoreTestsActivator.getDefault().getBundleContext();
reg1 = bundleContext.registerService(PrintService.SERVICE_NAME, stringPrint1, null);
ContextInjectionFactory.inject(userObject, context);
userObject.print("test");
assertEquals("1.0", "test", stringPrint1.toString());
// now remove the service
reg1.unregister();
reg1 = null;
userObject.print("another test");
// the string should be unchanged
assertEquals("1.1", "test", stringPrint1.toString());
assertNull("1.2", userObject.printer);
// register a different service implementation
StringPrintService stringPrint2 = new StringPrintService();
reg2 = bundleContext.registerService(PrintService.SERVICE_NAME, stringPrint2, null);
userObject.print("yet another test");
// the second string should have the value
assertEquals("2.0", "test", stringPrint1.toString());
assertEquals("2.1", "yet another test", stringPrint2.toString());
reg2.unregister();
reg2 = null;
assertNull("2.2", userObject.printer);
} finally {
if (reg1 != null) {
reg1.unregister();
reg1 = null;
}
if (reg2 != null) {
reg2.unregister();
reg2 = null;
}
}
}
@Test
public void testServiceAddition() {
ServiceRegistration<?> reg1 = null;
try {
Printer userObject = new Printer();
ContextInjectionFactory.inject(userObject, context);
StringPrintService stringPrint1 = new StringPrintService();
BundleContext bundleContext = CoreTestsActivator.getDefault().getBundleContext();
reg1 = bundleContext.registerService(PrintService.SERVICE_NAME, stringPrint1, null);
userObject.print("test");
assertEquals("1.0", "test", stringPrint1.toString());
} finally {
if (reg1 != null) {
reg1.unregister();
reg1 = null;
}
}
}
protected void ensureUnregistered(ServiceRegistration<?> reg) {
registrations.add(reg);
}
/**
* Tests that OSGi services are released when their context is disposed.
*/
@Test
public void testServiceRemovalOnContextDispose() {
StringPrintService stringPrint1 = new StringPrintService();
BundleContext bundleContext = CoreTestsActivator.getDefault()
.getBundleContext();
Bundle otherBundle = null;
for (Bundle b : bundleContext.getBundles()) {
if (b.getSymbolicName().equals("org.eclipse.core.tests.harness")) {
otherBundle = b;
break;
}
}
assertNotNull(otherBundle);
IEclipseContext otherServiceContext = EclipseContextFactory
.getServiceContext(otherBundle.getBundleContext());
ServiceRegistration<?> reg1 = bundleContext.registerService(
PrintService.SERVICE_NAME, stringPrint1, null);
try {
ServiceReference<?> ref = reg1.getReference();
PrintService service = (PrintService) otherServiceContext
.get(PrintService.SERVICE_NAME);
assertEquals("1.0", stringPrint1, service);
assertEquals("1.1", 1, ref.getUsingBundles().length);
service = null;
otherServiceContext.dispose();
assertNull("2.0", ref.getUsingBundles());
} finally {
reg1.unregister();
}
}
@Test
public void testRecursiveServiceRemoval() {
BundleContext bundleContext = CoreTestsActivator.getDefault().getBundleContext();
ServiceRegistration<?> reg1 = bundleContext.registerService(PrintService.SERVICE_NAME, new StringPrintService(), null);
final IEclipseContext child = context.createChild();
final IEclipseContext child2 = context.createChild();
child.get(PrintService.SERVICE_NAME);
child2.get(PrintService.SERVICE_NAME);
ensureUnregistered(reg1);
final boolean[] done = new boolean[] {false};
context.runAndTrack(new RunAndTrack() {
@Override
public boolean changed(IEclipseContext context) {
if (context.get(PrintService.SERVICE_NAME) == null) {
child.dispose();
done[0] = true;
}
return true;
}
});
reg1.unregister();
}
@Test
public void testServiceExample() {
BundleContext bundleContext = CoreTestsActivator.getDefault().getBundleContext();
ServiceRegistration<?> reg = bundleContext.registerService(IPaletteService.class.getName(), new PaletteImpl(Color.BLUE), null);
IEclipseContext context = EclipseContextFactory.getServiceContext(bundleContext);
Crayon crayon = new Crayon();
ContextInjectionFactory.inject(crayon, context);
crayon.draw();
reg.unregister();
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<TestServiceController> 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<TestServiceController> 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);
}
}
}