blob: 68f7ed3b904154d5edecc3aec463e20f4fd7c1d6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020 IBM Corporation 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:
* Alex Blewitt - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.common.tests;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.assertNotEquals;
import static org.osgi.framework.FrameworkUtil.asDictionary;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.runtime.ServiceCaller;
import org.eclipse.core.tests.harness.CoreTest;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
public class ServiceCallerTest extends CoreTest {
static class ServiceExampleFactory implements ServiceFactory<IServiceExample> {
final Map<Bundle, AtomicInteger> createCount = new ConcurrentHashMap<>();
volatile ServiceExample lastCreated;
@Override
public IServiceExample getService(Bundle bundle, ServiceRegistration<IServiceExample> registration) {
createCount.computeIfAbsent(bundle, (s) -> new AtomicInteger()).incrementAndGet();
return lastCreated = new ServiceExample();
}
@Override
public void ungetService(Bundle bundle, ServiceRegistration<IServiceExample> registration,
IServiceExample service) {
// do nothing
}
int getCreateCount(Bundle b) {
AtomicInteger result = createCount.get(b);
return result == null ? 0 : result.get();
}
}
/**
* Need a zero argument constructor to satisfy the test harness. This
* constructor should not do any real work nor should it be called by user code.
*/
public ServiceCallerTest() {
super(null);
}
public ServiceCallerTest(String name) {
super(name);
}
public void testCallOnce() {
Bundle bundle = FrameworkUtil.getBundle(ServiceCallerTest.class);
assertNotNull("Test only works under an OSGi runtime", bundle);
BundleContext context = bundle.getBundleContext();
ServiceExampleFactory factory = new ServiceExampleFactory();
ServiceRegistration<IServiceExample> reg = null;
try {
Dictionary<String, String> props = new Hashtable<>();
props.put("test", "value");
reg = context.registerService(IServiceExample.class, factory, props);
ServiceCaller.callOnce(IServiceExample.class, IServiceExample.class, IServiceExample::call);
ServiceExample lastCreated1 = factory.lastCreated;
assertTrue("Service called successfully", lastCreated1.called);
Bundle[] users = reg.getReference().getUsingBundles();
assertNull("Didn't expect users.", users);
ServiceCaller.callOnce(IServiceExample.class, IServiceExample.class, "(test=value)", IServiceExample::call);
ServiceExample lastCreated2 = factory.lastCreated;
assertTrue("Service called successfully", lastCreated2.called);
assertNotEquals("Should have new service each call", lastCreated1, lastCreated2);
boolean result = ServiceCaller.callOnce(IServiceExample.class, IServiceExample.class, "(test!=value)",
IServiceExample::call);
assertFalse("Should not have found service with filter", result);
assertEquals("Unexpected createCount", 2, factory.getCreateCount(bundle));
} finally {
reg.unregister();
}
}
public void testCall() throws IOException, BundleException, ClassNotFoundException {
Bundle bundle = FrameworkUtil.getBundle(ServiceCallerTest.class);
assertNotNull("Test only works under an OSGi runtime", bundle);
BundleContext context = bundle.getBundleContext();
Bundle testBundle = StatusTest.installTestClassBundle(context);
testBundle.start();
ServiceCaller<IServiceExample> thisClassCaller;
ServiceCaller<IServiceExample> thisClassCallerFilter;
ServiceCaller<IServiceExample> otherClassCaller;
thisClassCaller = new ServiceCaller<>(getClass(), IServiceExample.class);
thisClassCallerFilter = new ServiceCaller<>(getClass(), IServiceExample.class, "(test=value2)");
Class<?> testClass = testBundle.loadClass(TestClass.class.getName());
otherClassCaller = new ServiceCaller<>(testClass, IServiceExample.class);
assertFalse("Should not be called.", thisClassCaller.call(IServiceExample::call));
assertFalse("Should not be called.", thisClassCallerFilter.call(IServiceExample::call));
assertFalse("Should not be called.", otherClassCaller.call(IServiceExample::call));
assertFalse("Should not be present.", thisClassCaller.current().isPresent());
assertFalse("Should not be present.", thisClassCallerFilter.current().isPresent());
assertFalse("Should not be present.", otherClassCaller.current().isPresent());
ServiceExampleFactory factory = new ServiceExampleFactory();
Dictionary<String, String> props = new Hashtable<>();
props.put("test", "value1");
ServiceRegistration<IServiceExample> reg = context.registerService(IServiceExample.class, factory, props);
try {
assertTrue("Call returned false.", thisClassCaller.call(IServiceExample::call));
assertTrue("Service called successfully", factory.lastCreated.called);
assertTrue("Should be present.", thisClassCaller.current().isPresent());
assertEquals("Unexpected current.", thisClassCaller.current().get(), factory.lastCreated);
assertFalse("Should not be called.", thisClassCallerFilter.call(IServiceExample::call));
factory.lastCreated.called = false;
assertTrue("Call returned false.", otherClassCaller.call(IServiceExample::call));
assertTrue("Service called successfully", factory.lastCreated.called);
assertTrue("Call returned false.", thisClassCaller.call(IServiceExample::call));
assertTrue("Call returned false.", otherClassCaller.call(IServiceExample::call));
assertEquals("Wrong createCount", 1, factory.getCreateCount(bundle));
assertEquals("Wrong createCount", 1, factory.getCreateCount(testBundle));
Bundle[] users = reg.getReference().getUsingBundles();
assertNotNull("Expected some users", users);
Collection<Bundle> userCollection = Arrays.asList(users);
assertTrue("Missing bundle.", userCollection.contains(bundle));
assertTrue("Missing bundle.", userCollection.contains(testBundle));
reg.unregister();
assertFalse("Should not be called.", thisClassCaller.call(IServiceExample::call));
assertFalse("Should not be called.", otherClassCaller.call(IServiceExample::call));
assertFalse("Should not be present.", thisClassCaller.current().isPresent());
props.put("test", "value2");
reg = context.registerService(IServiceExample.class, factory, props);
assertTrue("Should be present.", thisClassCaller.current().isPresent());
assertEquals("Unexpected current.", thisClassCaller.current().get(), factory.lastCreated);
assertTrue("Call returned false.", thisClassCaller.call(IServiceExample::call));
assertTrue("Call returned false.", thisClassCallerFilter.call(IServiceExample::call));
assertTrue("Call returned false.", otherClassCaller.call(IServiceExample::call));
assertTrue("Call returned false.", thisClassCaller.call(IServiceExample::call));
assertTrue("Call returned false.", thisClassCallerFilter.call(IServiceExample::call));
assertTrue("Call returned false.", otherClassCaller.call(IServiceExample::call));
assertEquals("Wrong createCount", 2, factory.getCreateCount(bundle));
assertEquals("Wrong createCount", 2, factory.getCreateCount(testBundle));
testBundle.stop();
assertFalse("Should Not be called.", otherClassCaller.call(IServiceExample::call));
testBundle.start();
assertTrue("Call returned false.", otherClassCaller.call(IServiceExample::call));
assertEquals("Wrong createCount", 3, factory.getCreateCount(testBundle));
thisClassCaller.unget();
thisClassCallerFilter.unget();
assertTrue("Call returned false.", thisClassCaller.call(IServiceExample::call));
assertTrue("Call returned false.", thisClassCallerFilter.call(IServiceExample::call));
assertEquals("Wrong createCount", 3, factory.getCreateCount(bundle));
props.put("test", "value3");
reg.setProperties(props);
assertTrue("Call returned false.", thisClassCaller.call(IServiceExample::call));
assertFalse("Should not be called.", thisClassCallerFilter.call(IServiceExample::call));
assertFalse("Should not be present.", thisClassCallerFilter.current().isPresent());
} finally {
testBundle.uninstall();
thisClassCaller.unget();
otherClassCaller.unget();
try {
reg.unregister();
} catch (IllegalStateException e) {
// ignore
}
}
}
public void testInvalidFilter() {
try {
new ServiceCaller<>(getClass(), IServiceExample.class, "invalid filter");
fail("Expected an exception on invalid filter.");
} catch (IllegalArgumentException e) {
assertTrue("Unexpected cause.", e.getCause() instanceof InvalidSyntaxException);
}
try {
ServiceCaller.callOnce(getClass(), IServiceExample.class, "invalid filter", (example) -> {
});
fail("Expected an exception on invalid filter.");
} catch (IllegalArgumentException e) {
assertTrue("Unexpected cause.", e.getCause() instanceof InvalidSyntaxException);
}
}
public void testRank() {
Bundle bundle = FrameworkUtil.getBundle(ServiceCallerTest.class);
assertNotNull("Test only works under an OSGi runtime", bundle);
BundleContext context = bundle.getBundleContext();
ServiceExample s1 = new ServiceExample();
ServiceExample s2 = new ServiceExample();
ServiceExample s3 = new ServiceExample();
ServiceRegistration<IServiceExample> reg1 = null;
ServiceRegistration<IServiceExample> reg2 = null;
ServiceRegistration<IServiceExample> reg3 = null;
try {
reg1 = context.registerService(IServiceExample.class, s1,
asDictionary(singletonMap(Constants.SERVICE_RANKING, 1)));
reg2 = context.registerService(IServiceExample.class, s2,
asDictionary(singletonMap(Constants.SERVICE_RANKING, 2)));
reg3 = context.registerService(IServiceExample.class, s3,
asDictionary(singletonMap(Constants.SERVICE_RANKING, 3)));
ServiceCaller<IServiceExample> caller = new ServiceCaller<>(getClass(), IServiceExample.class);
assertHighestRankedCalled(caller, s3, s2, s1, reg3, reg2, reg1);
reg1.setProperties(asDictionary(singletonMap(Constants.SERVICE_RANKING, 10)));
assertHighestRankedCalled(caller, s1, s2, s3, reg1, reg2, reg3);
reg2.setProperties(asDictionary(singletonMap(Constants.SERVICE_RANKING, 10)));
reg1.setProperties(asDictionary(singletonMap(Constants.SERVICE_RANKING, 11)));
assertHighestRankedCalled(caller, s1, s2, s3, reg1, reg2, reg3);
reg1.setProperties(asDictionary(singletonMap(Constants.SERVICE_RANKING, 1)));
assertHighestRankedCalled(caller, s2, s1, s3, reg2, reg1, reg3);
} finally {
if (reg1 != null) {
reg1.unregister();
}
if (reg2 != null) {
reg2.unregister();
}
if (reg3 != null) {
reg3.unregister();
}
}
}
private void assertHighestRankedCalled(ServiceCaller<IServiceExample> caller, ServiceExample highest,
ServiceExample lower1, ServiceExample lower2, ServiceRegistration<IServiceExample> highestReg,
ServiceRegistration<IServiceExample> lower1Reg, ServiceRegistration<IServiceExample> lower2Reg) {
assertTrue("Did not call service.", caller.call(IServiceExample::call));
assertTrue("Highest Ranked not called.", highest.called);
assertFalse("Lower1 called.", lower1.called);
assertFalse("Lower2 called.", lower2.called);
assertNotNull("No users of highest.", highestReg.getReference().getUsingBundles());
assertNull("Users of lower ranked.", lower1Reg.getReference().getUsingBundles());
assertNull("Users of lower ranked.", lower2Reg.getReference().getUsingBundles());
highest.called = false;
lower1.called = false;
lower2.called = false;
}
interface IServiceExample {
void call();
}
static class ServiceExample implements IServiceExample {
boolean called = false;
@Override
public void call() {
called = true;
}
}
}