Bug 565304 - Add callOnce with filter

The ServiceCaller supports passing through an OSGi filter to reduce
the set of services considered for calling to be returned. However, this
is not exposed on the `callOnce` method.

Resolve this by adding a variant to `callOnce` that provides the
overload to pass through an OSGi filter.

Change-Id: Iab07a376e6aa4732372f8db84f351428e6f0bc45
Signed-off-by: Alex Blewitt <alex.blewitt@gmail.com>
diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/ServiceCallerTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/ServiceCallerTest.java
index 1be98fb..68f7ed3 100644
--- a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/ServiceCallerTest.java
+++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/ServiceCallerTest.java
@@ -78,7 +78,10 @@
 		ServiceExampleFactory factory = new ServiceExampleFactory();
 		ServiceRegistration<IServiceExample> reg = null;
 		try {
-			reg = context.registerService(IServiceExample.class, factory, null);
+			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);
@@ -86,12 +89,16 @@
 			Bundle[] users = reg.getReference().getUsingBundles();
 			assertNull("Didn't expect users.", users);
 
-			ServiceCaller.callOnce(IServiceExample.class, IServiceExample.class, IServiceExample::call);
+			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();
@@ -208,6 +215,13 @@
 		} 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() {
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java
index 655cfe8..9d79748 100644
--- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java
+++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ServiceCaller.java
@@ -126,6 +126,20 @@
 		return new ServiceCaller<>(caller, serviceType).getCallUnget(consumer);
 	}
 
+	/**
+	 * As {@link #callOnce(Class, Class, Consumer)} with an additional OSGi filter.
+	 * @param caller a class from the bundle that will use service
+	 * @param serviceType the OSGi service type to look up
+	 * @param consumer the consumer of the OSGi service
+	 * @param filter an OSGi filter to restrict the services found
+	 * @param <Service> the OSGi service type to look up
+	 * @return true if the OSGi service was located and called successfully, false otherwise
+	 * @throws NullPointerException if any of the parameters are null
+	 */
+	public static <Service> boolean callOnce(Class<?> caller, Class<Service> serviceType, String filter, Consumer<Service> consumer) {
+		return new ServiceCaller<>(caller, serviceType, filter).getCallUnget(consumer);
+	}
+
 	static int getRank(ServiceReference<?> ref) {
 		Object rank = ref.getProperty(Constants.SERVICE_RANKING);
 		if (rank instanceof Integer) {