diff options
7 files changed, 626 insertions, 166 deletions
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ServiceRegistryBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ServiceRegistryBundleTests.java index db6eeb2bb..0f35baa24 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ServiceRegistryBundleTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ServiceRegistryBundleTests.java @@ -10,11 +10,11 @@ *******************************************************************************/ package org.eclipse.osgi.tests.bundles; -import java.util.Hashtable; -import junit.framework.Test; -import junit.framework.TestSuite; +import java.util.*; +import junit.framework.*; import org.eclipse.osgi.tests.OSGiTestsActivator; import org.osgi.framework.*; +import org.osgi.framework.hooks.service.FindHook; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; @@ -24,6 +24,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceTracker01() { + final String testMethodName = "testServiceTracker01"; //$NON-NLS-1$ // simple ServiceTracker test Runnable runIt = new Runnable() { public void run() { @@ -31,7 +32,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } }; Hashtable props = new Hashtable(); - props.put("testServiceTracker01", Boolean.TRUE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.TRUE); ServiceRegistration reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runIt, props); ServiceTracker testTracker = null; try { @@ -51,7 +52,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } }; try { - testTracker = new ServiceTracker(OSGiTestsActivator.getContext(), FrameworkUtil.createFilter("(&(objectClass=java.lang.Runnable)(testServiceTracker01=true))"), testCustomizer); //$NON-NLS-1$ + testTracker = new ServiceTracker(OSGiTestsActivator.getContext(), FrameworkUtil.createFilter("(&(objectClass=java.lang.Runnable)(" + testMethodName + "=true))"), testCustomizer); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("filter error", e); //$NON-NLS-1$ } @@ -70,7 +71,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props to no longer match - props.put("testServiceTracker01", Boolean.FALSE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.FALSE); reg.setProperties(props); assertFalse("Did call addingService", results[0]); //$NON-NLS-1$ assertFalse("Did call modifiedService", results[1]); //$NON-NLS-1$ @@ -86,7 +87,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props back to match - props.put("testServiceTracker01", Boolean.TRUE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.TRUE); reg.setProperties(props); assertTrue("Did not call addingService", results[0]); //$NON-NLS-1$ assertFalse("Did call modifiedService", results[1]); //$NON-NLS-1$ @@ -102,6 +103,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceTracker02() { + final String testMethodName = "testServiceTracker02"; //$NON-NLS-1$ // simple ServiceTracker test Runnable runIt = new Runnable() { public void run() { @@ -109,7 +111,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } }; Hashtable props = new Hashtable(); - props.put("testServiceTracker02", Boolean.FALSE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.FALSE); ServiceRegistration reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runIt, props); ServiceTracker testTracker = null; try { @@ -129,7 +131,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } }; try { - testTracker = new ServiceTracker(OSGiTestsActivator.getContext(), FrameworkUtil.createFilter("(&(objectClass=java.lang.Runnable)(testServiceTracker02=true))"), testCustomizer); //$NON-NLS-1$ + testTracker = new ServiceTracker(OSGiTestsActivator.getContext(), FrameworkUtil.createFilter("(&(objectClass=java.lang.Runnable)(" + testMethodName + "=true))"), testCustomizer); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("filter error", e); //$NON-NLS-1$ } @@ -140,7 +142,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props to match - props.put("testServiceTracker02", Boolean.TRUE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.TRUE); reg.setProperties(props); assertTrue("Did not call addingService", results[0]); //$NON-NLS-1$ assertFalse("Did call modifiedService", results[1]); //$NON-NLS-1$ @@ -156,7 +158,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props to no longer match - props.put("testServiceTracker02", Boolean.FALSE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.FALSE); reg.setProperties(props); assertFalse("Did call addingService", results[0]); //$NON-NLS-1$ assertFalse("Did call modifiedService", results[1]); //$NON-NLS-1$ @@ -180,6 +182,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceListener01() { + final String testMethodName = "testServiceListener01"; //$NON-NLS-1$ // simple ServiceListener test Runnable runIt = new Runnable() { public void run() { @@ -206,7 +209,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } }; try { - OSGiTestsActivator.getContext().addServiceListener(testListener, "(&(objectClass=java.lang.Runnable)(testServiceListener01=true))"); //$NON-NLS-1$ + OSGiTestsActivator.getContext().addServiceListener(testListener, "(&(objectClass=java.lang.Runnable)(" + testMethodName + "=true))"); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("filter error", e); //$NON-NLS-1$ } @@ -214,7 +217,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { try { // register service which matches Hashtable props = new Hashtable(); - props.put("testServiceListener01", Boolean.TRUE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.TRUE); reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runIt, props); assertTrue("Did not get ServiceEvent.REGISTERED", results[0]); //$NON-NLS-1$ assertFalse("Did get ServiceEvent.MODIFIED", results[1]); //$NON-NLS-1$ @@ -232,7 +235,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props to no longer match - props.put("testServiceListener01", Boolean.FALSE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.FALSE); reg.setProperties(props); assertFalse("Did get ServiceEvent.REGISTERED", results[0]); //$NON-NLS-1$ assertFalse("Did get ServiceEvent.MODIFIED", results[1]); //$NON-NLS-1$ @@ -250,7 +253,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props back to match - props.put("testServiceListener01", Boolean.TRUE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.TRUE); reg.setProperties(props); assertFalse("Did get ServiceEvent.REGISTERED", results[0]); //$NON-NLS-1$ assertTrue("Did not get ServiceEvent.MODIFIED", results[1]); //$NON-NLS-1$ @@ -274,6 +277,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceListener02() { + final String testMethodName = "testServiceListener02"; //$NON-NLS-1$ // simple ServiceListener test Runnable runIt = new Runnable() { public void run() { @@ -300,7 +304,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } }; try { - OSGiTestsActivator.getContext().addServiceListener(testListener, "(&(objectClass=java.lang.Runnable)(testServiceListener02=true))"); //$NON-NLS-1$ + OSGiTestsActivator.getContext().addServiceListener(testListener, "(&(objectClass=java.lang.Runnable)(" + testMethodName + "=true))"); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("filter error", e); //$NON-NLS-1$ } @@ -308,7 +312,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { try { // register service which does not match Hashtable props = new Hashtable(); - props.put("testServiceListener02", Boolean.FALSE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.FALSE); reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runIt, props); assertFalse("Did get ServiceEvent.REGISTERED", results[0]); //$NON-NLS-1$ assertFalse("Did get ServiceEvent.MODIFIED", results[1]); //$NON-NLS-1$ @@ -326,7 +330,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props to match - props.put("testServiceListener02", Boolean.TRUE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.TRUE); reg.setProperties(props); assertFalse("Did get ServiceEvent.REGISTERED", results[0]); //$NON-NLS-1$ assertTrue("Did not get ServiceEvent.MODIFIED", results[1]); //$NON-NLS-1$ @@ -344,7 +348,7 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { clearResults(results); // change props to no longer match - props.put("testServiceListener02", Boolean.FALSE); //$NON-NLS-1$ + props.put(testMethodName, Boolean.FALSE); reg.setProperties(props); assertFalse("Did get ServiceEvent.REGISTERED", results[0]); //$NON-NLS-1$ assertFalse("Did get ServiceEvent.MODIFIED", results[1]); //$NON-NLS-1$ @@ -368,17 +372,18 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceException01() { + final String testMethodName = "testServiceException01"; //$NON-NLS-1$ // test a service factory which returns wrong object types ServiceExceptionServiceFactory wrongObjectFactory = new ServiceExceptionServiceFactory("A String"); //$NON-NLS-1$ Hashtable props = new Hashtable(); - props.put("name", "testServiceException01"); //$NON-NLS-1$ //$NON-NLS-2$ + props.put("name", testMethodName); //$NON-NLS-1$ ServiceRegistration reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), wrongObjectFactory, props); ServiceExceptionFrameworkListener listener = new ServiceExceptionFrameworkListener(OSGiTestsActivator.getContext().getBundle(), null, ServiceException.FACTORY_ERROR); OSGiTestsActivator.getContext().addFrameworkListener(listener); try { ServiceReference[] refs = null; try { - refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=testServiceException01)"); //$NON-NLS-1$ + refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=" + testMethodName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("Unexpected syntax error", e); //$NON-NLS-1$ } @@ -405,17 +410,18 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceException02() { + final String testMethodName = "testServiceException02"; //$NON-NLS-1$ // test a service factory which returns null objects ServiceExceptionServiceFactory nullObjectFactory = new ServiceExceptionServiceFactory(null); Hashtable props = new Hashtable(); - props.put("name", "testServiceException02"); //$NON-NLS-1$ //$NON-NLS-2$ + props.put("name", testMethodName); //$NON-NLS-1$ ServiceRegistration reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), nullObjectFactory, props); ServiceExceptionFrameworkListener listener = new ServiceExceptionFrameworkListener(OSGiTestsActivator.getContext().getBundle(), null, ServiceException.FACTORY_ERROR); OSGiTestsActivator.getContext().addFrameworkListener(listener); try { ServiceReference[] refs = null; try { - refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=testServiceException02)"); //$NON-NLS-1$ + refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=" + testMethodName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("Unexpected syntax error", e); //$NON-NLS-1$ } @@ -442,18 +448,19 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceException03() { + final String testMethodName = "testServiceException03"; //$NON-NLS-1$ // test a service factory which throws a RuntimeException - RuntimeException cause = new RuntimeException("testServiceException03"); //$NON-NLS-1$ + RuntimeException cause = new RuntimeException(testMethodName); ServiceExceptionServiceFactory runtimeExceptionFactory = new ServiceExceptionServiceFactory(cause); Hashtable props = new Hashtable(); - props.put("name", "testServiceException03"); //$NON-NLS-1$ //$NON-NLS-2$ + props.put("name", testMethodName); //$NON-NLS-1$ ServiceRegistration reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runtimeExceptionFactory, props); ServiceExceptionFrameworkListener listener = new ServiceExceptionFrameworkListener(OSGiTestsActivator.getContext().getBundle(), cause, ServiceException.FACTORY_EXCEPTION); OSGiTestsActivator.getContext().addFrameworkListener(listener); try { ServiceReference[] refs = null; try { - refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=testServiceException03)"); //$NON-NLS-1$ + refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=" + testMethodName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("Unexpected syntax error", e); //$NON-NLS-1$ } @@ -480,18 +487,19 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } public void testServiceException04() { + final String testMethodName = "testServiceException04"; //$NON-NLS-1$ // test a service factory which throws an Error - Error cause = new Error("testServiceException04"); //$NON-NLS-1$ + Error cause = new Error(testMethodName); ServiceExceptionServiceFactory errorFactory = new ServiceExceptionServiceFactory(cause); Hashtable props = new Hashtable(); - props.put("name", "testServiceException03"); //$NON-NLS-1$ //$NON-NLS-2$ + props.put("name", testMethodName); //$NON-NLS-1$ ServiceRegistration reg = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), errorFactory, props); ServiceExceptionFrameworkListener listener = new ServiceExceptionFrameworkListener(OSGiTestsActivator.getContext().getBundle(), cause, ServiceException.FACTORY_EXCEPTION); OSGiTestsActivator.getContext().addFrameworkListener(listener); try { ServiceReference[] refs = null; try { - refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=testServiceException03)"); //$NON-NLS-1$ + refs = OSGiTestsActivator.getContext().getServiceReferences(Runnable.class.getName(), "(name=" + testMethodName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } catch (InvalidSyntaxException e) { fail("Unexpected syntax error", e); //$NON-NLS-1$ } @@ -517,6 +525,317 @@ public class ServiceRegistryBundleTests extends AbstractBundleTests { } } + public void testServiceOrdering01() { + final String testMethodName = "testServiceOrdering01"; //$NON-NLS-1$ + // test that getServiceReference returns the proper service + Runnable runIt = new Runnable() { + public void run() { + // nothing + } + }; + Hashtable props = new Hashtable(); + props.put("name", testMethodName); //$NON-NLS-1$ + props.put(Constants.SERVICE_DESCRIPTION, "min value"); //$NON-NLS-1$ + props.put(Constants.SERVICE_RANKING, new Integer(Integer.MIN_VALUE)); + ServiceRegistration reg1 = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runIt, props); + + props.put(Constants.SERVICE_DESCRIPTION, "max value first"); //$NON-NLS-1$ + props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE)); + ServiceRegistration reg2 = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runIt, props); + + props.put(Constants.SERVICE_DESCRIPTION, "max value second"); //$NON-NLS-1$ + props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE)); + ServiceRegistration reg3 = OSGiTestsActivator.getContext().registerService(Runnable.class.getName(), runIt, props); + + try { + ServiceReference ref = null; + ref = OSGiTestsActivator.getContext().getServiceReference(Runnable.class.getName()); + assertNotNull("service ref is null", ref); //$NON-NLS-1$ + assertEquals("Wrong references", reg2.getReference(), ref); //$NON-NLS-1$ + } finally { + if (reg1 != null) + reg1.unregister(); + if (reg2 != null) + reg2.unregister(); + if (reg3 != null) + reg3.unregister(); + } + } + + public void testFindHook01() { + final String testMethodName = "testFindHook01"; //$NON-NLS-1$ + // test the FindHook is called and can remove a reference from the results + Runnable runIt = new Runnable() { + public void run() { + // nothing + } + }; + final BundleContext testContext = OSGiTestsActivator.getContext(); + // register services + Hashtable props = new Hashtable(); + props.put("name", testMethodName); //$NON-NLS-1$ + props.put(Constants.SERVICE_DESCRIPTION, "service 1"); //$NON-NLS-1$ + final ServiceRegistration reg1 = testContext.registerService(Runnable.class.getName(), runIt, props); + + props.put(Constants.SERVICE_DESCRIPTION, "service 2"); //$NON-NLS-1$ + final ServiceRegistration reg2 = testContext.registerService(Runnable.class.getName(), runIt, props); + + props.put(Constants.SERVICE_DESCRIPTION, "service 3"); //$NON-NLS-1$ + final ServiceRegistration reg3 = testContext.registerService(Runnable.class.getName(), runIt, props); + + final int[] hookCalled = new int[] {0, 0, 0, 0, 0}; + final AssertionFailedError[] hookError = new AssertionFailedError[] {null, null, null, null}; + + // register find hook 1 + props.put(Constants.SERVICE_DESCRIPTION, "find hook 1"); //$NON-NLS-1$ + props.put(Constants.SERVICE_DESCRIPTION, "min value"); //$NON-NLS-1$ + props.put(Constants.SERVICE_RANKING, new Integer(Integer.MIN_VALUE)); + ServiceRegistration regHook1 = testContext.registerService(FindHook.class.getName(), new FindHook() { + public void find(BundleContext context, String name, String filter, boolean allServices, Collection references) { + try { + synchronized (hookCalled) { + hookCalled[++hookCalled[0]] = 1; + } + assertEquals("wrong context in hook", testContext, context); //$NON-NLS-1$ + assertEquals("wrong name in hook", Runnable.class.getName(), name); //$NON-NLS-1$ + assertEquals("wrong filter in hook", "(name=" + testMethodName + ")", filter); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + assertEquals("wrong allservices in hook", false, allServices); //$NON-NLS-1$ + assertEquals("wrong number of services in hook", 1, references.size()); //$NON-NLS-1$ + Iterator iter = references.iterator(); + while (iter.hasNext()) { + ServiceReference ref = (ServiceReference) iter.next(); + if (ref.equals(reg1.getReference())) { + fail("service 1 is present"); //$NON-NLS-1$ + } + if (ref.equals(reg2.getReference())) { + fail("service 2 is present"); //$NON-NLS-1$ + } + } + + try { + references.add(reg1.getReference()); + fail("add to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + try { + references.addAll(Arrays.asList(new ServiceReference[] {reg1.getReference()})); + fail("addAll to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + } catch (AssertionFailedError a) { + hookError[0] = a; + return; + } + } + }, props); + + // register find hook 2 + props.put(Constants.SERVICE_DESCRIPTION, "find hook 2"); //$NON-NLS-1$ + props.put(Constants.SERVICE_DESCRIPTION, "max value first"); //$NON-NLS-1$ + props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE)); + ServiceRegistration regHook2 = testContext.registerService(FindHook.class.getName(), new FindHook() { + public void find(BundleContext context, String name, String filter, boolean allServices, Collection references) { + try { + synchronized (hookCalled) { + hookCalled[++hookCalled[0]] = 2; + } + assertEquals("wrong context in hook", testContext, context); //$NON-NLS-1$ + assertEquals("wrong name in hook", Runnable.class.getName(), name); //$NON-NLS-1$ + assertEquals("wrong filter in hook", "(name=" + testMethodName + ")", filter); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + assertEquals("wrong allservices in hook", false, allServices); //$NON-NLS-1$ + assertEquals("wrong number of services in hook", 3, references.size()); //$NON-NLS-1$ + Iterator iter = references.iterator(); + while (iter.hasNext()) { + ServiceReference ref = (ServiceReference) iter.next(); + if (ref.equals(reg2.getReference())) { + iter.remove(); + } + } + + try { + references.add(reg2.getReference()); + fail("add to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + try { + references.addAll(Arrays.asList(new ServiceReference[] {reg2.getReference()})); + fail("addAll to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + } catch (AssertionFailedError a) { + hookError[1] = a; + return; + } + } + }, props); + + // register find hook 3 + props.put(Constants.SERVICE_DESCRIPTION, "find hook 3"); //$NON-NLS-1$ + props.put(Constants.SERVICE_DESCRIPTION, "max value second"); //$NON-NLS-1$ + props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE)); + ServiceRegistration regHook3 = testContext.registerService(FindHook.class.getName(), new FindHook() { + public void find(BundleContext context, String name, String filter, boolean allServices, Collection references) { + try { + synchronized (hookCalled) { + hookCalled[++hookCalled[0]] = 3; + } + assertEquals("wrong context in hook", testContext, context); //$NON-NLS-1$ + assertEquals("wrong name in hook", Runnable.class.getName(), name); //$NON-NLS-1$ + assertEquals("wrong filter in hook", "(name=" + testMethodName + ")", filter); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + assertEquals("wrong allservices in hook", false, allServices); //$NON-NLS-1$ + assertEquals("wrong number of services in hook", 2, references.size()); //$NON-NLS-1$ + Iterator iter = references.iterator(); + while (iter.hasNext()) { + ServiceReference ref = (ServiceReference) iter.next(); + if (ref.equals(reg2.getReference())) { + fail("service 2 is present"); //$NON-NLS-1$ + } + } + + try { + references.add(reg2.getReference()); + fail("add to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + try { + references.addAll(Arrays.asList(new ServiceReference[] {reg2.getReference()})); + fail("addAll to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + } catch (AssertionFailedError a) { + hookError[2] = a; + return; + } + // throw an exception from the hook to test that the next hooks are called. + throw new RuntimeException(testMethodName); + } + }, props); + + // register find hook 4 + props.put(Constants.SERVICE_DESCRIPTION, "find hook 4"); //$NON-NLS-1$ + props.put(Constants.SERVICE_DESCRIPTION, "max value third"); //$NON-NLS-1$ + props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE)); + ServiceRegistration regHook4 = testContext.registerService(FindHook.class.getName(), new FindHook() { + public void find(BundleContext context, String name, String filter, boolean allServices, Collection references) { + try { + synchronized (hookCalled) { + hookCalled[++hookCalled[0]] = 4; + } + assertEquals("wrong context in hook", testContext, context); //$NON-NLS-1$ + assertEquals("wrong name in hook", Runnable.class.getName(), name); //$NON-NLS-1$ + assertEquals("wrong filter in hook", "(name=" + testMethodName + ")", filter); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + assertEquals("wrong allservices in hook", false, allServices); //$NON-NLS-1$ + assertEquals("wrong number of services in hook", 2, references.size()); //$NON-NLS-1$ + Iterator iter = references.iterator(); + while (iter.hasNext()) { + ServiceReference ref = (ServiceReference) iter.next(); + if (ref.equals(reg1.getReference())) { + iter.remove(); + } + if (ref.equals(reg2.getReference())) { + fail("service 2 is present"); //$NON-NLS-1$ + } + } + + try { + references.add(reg2.getReference()); + fail("add to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + try { + references.addAll(Arrays.asList(new ServiceReference[] {reg2.getReference()})); + fail("addAll to collection succeeded"); //$NON-NLS-1$ + } catch (Exception e) { + // should get an exception + } + } catch (AssertionFailedError a) { + hookError[3] = a; + return; + } + } + }, props); + + // get reference and hook removes some services + try { + ServiceReference[] refs = null; + try { + refs = testContext.getServiceReferences(Runnable.class.getName(), "(name=" + testMethodName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (InvalidSyntaxException e) { + fail("Unexpected syntax error", e); //$NON-NLS-1$ + } + assertEquals("all hooks not called", 4, hookCalled[0]); //$NON-NLS-1$ + assertEquals("hook 2 not called first", 2, hookCalled[1]); //$NON-NLS-1$ + assertEquals("hook 3 not called second", 3, hookCalled[2]); //$NON-NLS-1$ + assertEquals("hook 4 not called third", 4, hookCalled[3]); //$NON-NLS-1$ + assertEquals("hook 1 not called fourth ", 1, hookCalled[4]); //$NON-NLS-1$ + for (int i = 0; i < hookError.length; i++) { + if (hookError[i] != null) { + throw hookError[i]; + } + } + assertNotNull("service refs is null", refs); //$NON-NLS-1$ + assertEquals("Wrong number of references", 1, refs.length); //$NON-NLS-1$ + + // test removed services are not in the result + List refList = Arrays.asList(refs); + assertFalse("contains service 1", refList.contains(reg1.getReference())); //$NON-NLS-1$ + assertFalse("contains service 2", refList.contains(reg2.getReference())); //$NON-NLS-1$ + assertTrue("missing service 3", refList.contains(reg3.getReference())); //$NON-NLS-1$ + + // remove the hooks + regHook1.unregister(); + regHook1 = null; + regHook2.unregister(); + regHook2 = null; + regHook3.unregister(); + regHook3 = null; + regHook4.unregister(); + regHook4 = null; + + // get services and make sure none are filtered + refs = null; + hookCalled[0] = 0; + + try { + refs = testContext.getServiceReferences(Runnable.class.getName(), "(name=" + testMethodName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (InvalidSyntaxException e) { + fail("Unexpected syntax error", e); //$NON-NLS-1$ + } + assertEquals("hooks called", 0, hookCalled[0]); //$NON-NLS-1$ + assertNotNull("service refs is null", refs); //$NON-NLS-1$ + assertEquals("Wrong number of references", 3, refs.length); //$NON-NLS-1$ + + // test result contains all expected services + refList = Arrays.asList(refs); + assertTrue("missing service 1", refList.contains(reg1.getReference())); //$NON-NLS-1$ + assertTrue("missing service 2", refList.contains(reg2.getReference())); //$NON-NLS-1$ + assertTrue("missing service 3", refList.contains(reg3.getReference())); //$NON-NLS-1$ + } finally { + // unregister hook and services + if (regHook1 != null) + regHook1.unregister(); + if (regHook2 != null) + regHook2.unregister(); + if (regHook3 != null) + regHook3.unregister(); + if (regHook4 != null) + regHook4.unregister(); + if (reg1 != null) + reg1.unregister(); + if (reg2 != null) + reg2.unregister(); + if (reg3 != null) + reg3.unregister(); + } + } + private void clearResults(boolean[] results) { for (int i = 0; i < results.length; i++) results[i] = false; diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java index d401374f4..76b4c0a4c 100644 --- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java +++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java @@ -210,7 +210,7 @@ public class Framework implements EventDispatcher, EventPublisher, Runnable { if (Profile.PROFILE && Profile.STARTUP) Profile.logTime("Framework.initialze()", "done new EventManager"); //$NON-NLS-1$ //$NON-NLS-2$ /* create the service registry */ - serviceRegistry = new ServiceRegistry(); + serviceRegistry = new ServiceRegistry(this); // Initialize the installLock; there is no way of knowing // what the initial size should be, at most it will be the number // of threads trying to install a bundle (probably a very low number). diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceReferenceImpl.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceReferenceImpl.java index 954e94b2f..345924288 100755 --- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceReferenceImpl.java +++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceReferenceImpl.java @@ -188,7 +188,7 @@ public class ServiceReferenceImpl implements ServiceReference, Comparable { * {@link Constants#SERVICE_ID service id} and greater if it has a lower * service id. * - * @param reference The <code>ServiceReference</code> to be compared. + * @param object The <code>ServiceReference</code> to be compared. * @return Returns a negative integer, zero, or a positive integer if this * <code>ServiceReference</code> is less than, equal to, or * greater than the specified <code>ServiceReference</code>. @@ -196,9 +196,7 @@ public class ServiceReferenceImpl implements ServiceReference, Comparable { */ public int compareTo(Object object) { ServiceReferenceImpl other = (ServiceReferenceImpl) object; - if (this.getRanking() != other.getRanking()) - return this.getRanking() > other.getRanking() ? -1 : 1; - return this.getId() == other.getId() ? 0 : this.getId() > other.getId() ? 1 : -1; + return registration.compareTo(other.registration); } /** @@ -219,11 +217,11 @@ public class ServiceReferenceImpl implements ServiceReference, Comparable { */ public boolean equals(Object obj) { if (obj == this) { - return (true); + return true; } if (!(obj instanceof ServiceReferenceImpl)) { - return (false); + return false; } ServiceReferenceImpl other = (ServiceReferenceImpl) obj; @@ -257,22 +255,4 @@ public class ServiceReferenceImpl implements ServiceReference, Comparable { String[] getClasses() { return registration.getClasses(); } - - /** - * Return the service id of the ServiceRegistration. - * - * @return service.id of the service - */ - long getId() { - return registration.getId(); - } - - /** - * Return the service ranking of the ServiceRegistration. - * - * @return service.ranking of the service - */ - int getRanking() { - return registration.getRanking(); - } } diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistrationImpl.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistrationImpl.java index 7a8db27f8..58e1a6f14 100755 --- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistrationImpl.java +++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistrationImpl.java @@ -34,7 +34,7 @@ import org.osgi.framework.Constants; * * @ThreadSafe */ -public class ServiceRegistrationImpl implements ServiceRegistration { +public class ServiceRegistrationImpl implements ServiceRegistration, Comparable { /** Internal framework object. */ private final Framework framework; @@ -56,17 +56,18 @@ public class ServiceRegistrationImpl implements ServiceRegistration { /* @GuardedBy("registrationLock") */ private ServiceReferenceImpl reference; - /** list of contexts using the service. */ + /** List of contexts using the service. + * List<BundleContextImpl>. + * */ /* @GuardedBy("registrationLock") */ - private ArrayList contextsUsing; + private List contextsUsing; /** properties for this registration. */ /* @GuardedBy("registrationLock") */ private ServiceProperties properties; /** service id. */ - /* @GuardedBy("registrationLock") */ - private long serviceid; + private final long serviceid; /** service ranking. */ /* @GuardedBy("registrationLock") */ @@ -94,7 +95,9 @@ public class ServiceRegistrationImpl implements ServiceRegistration { this.framework = context.getFramework(); this.clazzes = clazzes; /* must be set before calling createProperties. */ this.service = service; + synchronized (registrationLock) { + this.serviceid = registry.getNextServiceId(); /* must be set before calling createProperties. */ this.contextsUsing = null; /* We leak this from the constructor here, but it is ok * because the ServiceReferenceImpl constructor only @@ -114,7 +117,6 @@ public class ServiceRegistrationImpl implements ServiceRegistration { context.checkValid(); synchronized (registrationLock) { ref = reference; /* used to publish event outside sync */ - serviceid = registry.getNextServiceId(); /* must be set before calling createProperties. */ this.properties = createProperties(props); /* must be valid after unregister is called. */ } if (Debug.DEBUG && Debug.DEBUG_SERVICES) { @@ -347,9 +349,7 @@ public class ServiceRegistrationImpl implements ServiceRegistration { * @return The service id for this service. */ long getId() { - synchronized (registrationLock) { - return serviceid; - } + return serviceid; } /** @@ -366,7 +366,7 @@ public class ServiceRegistrationImpl implements ServiceRegistration { return clazzes; } - Object getService() { + Object getServiceObject() { return service; } @@ -596,4 +596,36 @@ public class ServiceRegistrationImpl implements ServiceRegistration { return sb.toString(); } + + /** + * Compares this <code>ServiceRegistrationImpl</code> with the specified + * <code>ServiceRegistrationImpl</code> for order. + * + * <p> + * If this <code>ServiceRegistrationImpl</code> and the specified + * <code>ServiceRegistrationImpl</code> have the same + * {@link Constants#SERVICE_ID service id} they are equal. This + * <code>ServiceRegistrationImpl</code> is less than the specified + * <code>ServiceRegistrationImpl</code> if it has a lower + * {@link Constants#SERVICE_RANKING service ranking} and greater if it has a + * higher service ranking. Otherwise, if this <code>ServiceRegistrationImpl</code> + * and the specified <code>ServiceRegistrationImpl</code> have the same + * {@link Constants#SERVICE_RANKING service ranking}, this + * <code>ServiceRegistrationImpl</code> is less than the specified + * <code>ServiceRegistrationImpl</code> if it has a higher + * {@link Constants#SERVICE_ID service id} and greater if it has a lower + * service id. + * + * @param object The <code>ServiceRegistrationImpl</code> to be compared. + * @return Returns a negative integer, zero, or a positive integer if this + * <code>ServiceRegistrationImpl</code> is less than, equal to, or + * greater than the specified <code>ServiceRegistrationImpl</code>. + */ + public int compareTo(Object object) { + ServiceRegistrationImpl other = (ServiceRegistrationImpl) object; + + if (this.getRanking() != other.getRanking()) + return this.getRanking() > other.getRanking() ? -1 : 1; + return this.getId() == other.getId() ? 0 : this.getId() > other.getId() ? 1 : -1; + } } diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java index d159302d4..33bdad7d9 100755 --- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java +++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java @@ -18,6 +18,7 @@ import org.eclipse.osgi.framework.internal.core.*; import org.eclipse.osgi.util.NLS; import org.osgi.framework.*; import org.osgi.framework.Constants; +import org.osgi.framework.hooks.service.FindHook; /** * The Service Registry. This class is the main control point for service @@ -29,26 +30,39 @@ public class ServiceRegistry { public static final String PROP_SCOPE_SERVICE_EVENTS = "osgi.scopeServiceEvents"; //$NON-NLS-1$ public static final boolean scopeEvents = Boolean.valueOf(FrameworkProperties.getProperty(PROP_SCOPE_SERVICE_EVENTS, "true")).booleanValue(); //$NON-NLS-1$ - /** Published services by class name. Key is a String class name; Value is a ArrayList of ServiceRegistrations */ + /** Published services by class name. + * Map<String,List<ServiceRegistrationImpl>> + * The List<ServiceRegistrationImpl>s are sorted. + */ /* @GuardedBy("this") */ - private final HashMap/*<String,ArrayList<ServiceRegistrationImpl>>*/publishedServicesByClass; - /** All published services. Value is ServiceRegistrations */ + private final Map/*<String,List<ServiceRegistrationImpl>>*/publishedServicesByClass; + /** All published services. + * List<ServiceRegistrationImpl>. + * The List<ServiceRegistrationImpl> is sorted. + */ /* @GuardedBy("this") */ - private final ArrayList/*<ServiceRegistrationImpl>*/allPublishedServices; - /** Published services by BundleContext. Key is a BundleContext; Value is a ArrayList of ServiceRegistrations*/ + private final List/*<ServiceRegistrationImpl>*/allPublishedServices; + /** Published services by BundleContextImpl. + * Map<BundleContextImpl,List<ServiceRegistrationImpl>>. + * The List<ServiceRegistrationImpl>s are NOT sorted. + */ /* @GuardedBy("this") */ - private final HashMap/*<BundleContextImpl,ArrayList<ServiceRegistrationImpl>*/publishedServicesByContext; + private final Map/*<BundleContextImpl,List<ServiceRegistrationImpl>>*/publishedServicesByContext; /** next free service id. */ /* @GuardedBy("this") */ private long serviceid; /** initial capacity of the data structure */ private static final int initialCapacity = 50; + /** framework which created this service registry */ + private final Framework framework; + /** * Initializes the internal data structures of this ServiceRegistry. * */ - public ServiceRegistry() { + public ServiceRegistry(Framework framework) { + this.framework = framework; serviceid = 1; publishedServicesByClass = new HashMap(initialCapacity); publishedServicesByContext = new HashMap(initialCapacity); @@ -58,8 +72,8 @@ public class ServiceRegistry { /** * Registers the specified service object with the specified properties * under the specified class names into the Framework. A - * <code>ServiceRegistration</code> object is returned. The - * <code>ServiceRegistration</code> object is for the private use of the + * <code>ServiceRegistrationImpl</code> object is returned. The + * <code>ServiceRegistrationImpl</code> object is for the private use of the * bundle registering the service and should not be shared with other * bundles. The registering bundle is defined to be the context bundle. * Other bundles can locate the service by using either the @@ -106,7 +120,7 @@ public class ServiceRegistry { * The set of properties may be <code>null</code> if the service * has no properties. * - * @return A <code>ServiceRegistration</code> object for use by the bundle + * @return A <code>ServiceRegistrationImpl</code> object for use by the bundle * registering the service to update the service's properties or to * unregister the service. * @@ -174,12 +188,11 @@ public class ServiceRegistry { ServiceRegistrationImpl registration = new ServiceRegistrationImpl(this, context, clazzes, service); registration.register(properties); return registration; - } /** - * Returns an array of <code>ServiceReference</code> objects. The returned - * array of <code>ServiceReference</code> objects contains services that + * Returns an array of <code>ServiceReferenceImpl</code> objects. The returned + * array of <code>ServiceReferenceImpl</code> objects contains services that * were registered under the specified class, match the specified filter * criteria, and the packages for the class names under which the services * were registered match the context bundle's packages as defined in @@ -203,19 +216,19 @@ public class ServiceRegistry { * * <p> * The following steps are required to select a set of - * <code>ServiceReference</code> objects: + * <code>ServiceReferenceImpl</code> objects: * <ol> * <li>If the filter string is not <code>null</code>, the filter string - * is parsed and the set <code>ServiceReference</code> objects of + * is parsed and the set <code>ServiceReferenceImpl</code> objects of * registered services that satisfy the filter is produced. If the filter * string is <code>null</code>, then all registered services are * considered to satisfy the filter. * <li>If the Java Runtime Environment supports permissions, the set of - * <code>ServiceReference</code> objects produced by the previous step is + * <code>ServiceReferenceImpl</code> objects produced by the previous step is * reduced by checking that the caller has the * <code>ServicePermission</code> to get at least one of the class names * under which the service was registered. If the caller does not have the - * correct permission for a particular <code>ServiceReference</code> + * correct permission for a particular <code>ServiceReferenceImpl</code> * object, then it is removed from the set. * <li>If <code>clazz</code> is not <code>null</code>, the set is * further reduced to those services that are an <code>instanceof</code> @@ -227,12 +240,12 @@ public class ServiceRegistry { * <code>ServiceReference</code> object and calling * {@link ServiceReference#isAssignableTo(Bundle, String)} with the context * bundle and each class name under which the <code>ServiceReference</code> - * object was registered. For any given <code>ServiceReference</code> + * object was registered. For any given <code>ServiceReferenceImpl</code> * object, if any call to * {@link ServiceReference#isAssignableTo(Bundle, String)} returns * <code>false</code>, then it is removed from the set of - * <code>ServiceReference</code> objects. - * <li>An array of the remaining <code>ServiceReference</code> objects is + * <code>ServiceReferenceImpl</code> objects. + * <li>An array of the remaining <code>ServiceReferenceImpl</code> objects is * returned. * </ol> * @@ -241,7 +254,7 @@ public class ServiceRegistry { * <code>null</code> for all services. * @param filterstring The filter criteria. * @param allservices True if the bundle called getAllServiceReferences. - * @return An array of <code>ServiceReference</code> objects or + * @return An array of <code>ServiceReferenceImpl</code> objects or * <code>null</code> if no services are registered which satisfy * the search. * @throws InvalidSyntaxException If <code>filter</code> contains an @@ -261,9 +274,9 @@ public class ServiceRegistry { } } Filter filter = (filterstring == null) ? null : context.createFilter(filterstring); - List references = null; + List references; synchronized (this) { - references = lookupServiceReferences(clazz, filter); + references = changeRegistrationsToReferences(lookupServiceRegistrations(clazz, filter)); Iterator iter = references.iterator(); while (iter.hasNext()) { ServiceReferenceImpl reference = (ServiceReferenceImpl) iter.next(); @@ -281,6 +294,8 @@ public class ServiceRegistry { } } + processFindHooks(context, clazz, filterstring, allservices, references); + int size = references.size(); if (size == 0) { return null; @@ -328,48 +343,9 @@ public class ServiceRegistry { ServiceReferenceImpl[] references = getServiceReferences(context, clazz, null, false); if (references != null) { - int index = 0; - - int length = references.length; - - if (length > 1) /* if more than one service, select highest ranking */{ - int rankings[] = new int[length]; - int count = 0; - int maxRanking = Integer.MIN_VALUE; - - for (int i = 0; i < length; i++) { - int ranking = references[i].getRanking(); - - rankings[i] = ranking; - - if (ranking > maxRanking) { - index = i; - maxRanking = ranking; - count = 1; - } else { - if (ranking == maxRanking) { - count++; - } - } - } - - if (count > 1) /* if still more than one service, select lowest id */{ - long minId = Long.MAX_VALUE; - - for (int i = 0; i < length; i++) { - if (rankings[i] == maxRanking) { - long id = references[i].getId(); - - if (id < minId) { - index = i; - minId = id; - } - } - } - } - } - - return references[index]; + // Since we maintain the registrations in a sorted List, the first element is always the + // correct one to return. + return references[0]; } } catch (InvalidSyntaxException e) { if (Debug.DEBUG && Debug.DEBUG_GENERAL) { @@ -517,7 +493,7 @@ public class ServiceRegistry { * @see ServicePermission */ public synchronized ServiceReferenceImpl[] getRegisteredServices(BundleContextImpl context) { - List references = lookupServiceReferences(context); + List references = changeRegistrationsToReferences(lookupServiceRegistrations(context)); ListIterator iter = references.listIterator(); while (iter.hasNext()) { ServiceReferenceImpl reference = (ServiceReferenceImpl) iter.next(); @@ -678,33 +654,39 @@ public class ServiceRegistry { */ /* @GuardedBy("this") */ void addServiceRegistration(BundleContextImpl context, ServiceRegistrationImpl registration) { - // Add the ServiceRegistration to the list of Services published by BundleContext. - ArrayList contextServices = (ArrayList) publishedServicesByContext.get(context); + // Add the ServiceRegistrationImpl to the list of Services published by BundleContextImpl. + List contextServices = (List) publishedServicesByContext.get(context); if (contextServices == null) { contextServices = new ArrayList(10); publishedServicesByContext.put(context, contextServices); } + // The list is NOT sorted, so we just add contextServices.add(registration); - // Add the ServiceRegistration to the list of Services published by Class Name. + // Add the ServiceRegistrationImpl to the list of Services published by Class Name. String[] clazzes = registration.getClasses(); int size = clazzes.length; + int insertIndex; for (int i = 0; i < size; i++) { String clazz = clazzes[i]; - ArrayList services = (ArrayList) publishedServicesByClass.get(clazz); + List services = (List) publishedServicesByClass.get(clazz); if (services == null) { services = new ArrayList(10); publishedServicesByClass.put(clazz, services); } - services.add(registration); + // The list is sorted, so we must find the proper location to insert + insertIndex = -Collections.binarySearch(services, registration) - 1; + services.add(insertIndex, registration); } - // Add the ServiceRegistration to the list of all published Services. - allPublishedServices.add(registration); + // Add the ServiceRegistrationImpl to the list of all published Services. + // The list is sorted, so we must find the proper location to insert + insertIndex = -Collections.binarySearch(allPublishedServices, registration) - 1; + allPublishedServices.add(insertIndex, registration); } /** @@ -715,55 +697,58 @@ public class ServiceRegistry { */ /* @GuardedBy("this") */ void removeServiceRegistration(BundleContextImpl context, ServiceRegistrationImpl serviceReg) { - // Remove the ServiceRegistration from the list of Services published by BundleContext. - ArrayList contextServices = (ArrayList) publishedServicesByContext.get(context); + // Remove the ServiceRegistrationImpl from the list of Services published by BundleContextImpl. + List contextServices = (List) publishedServicesByContext.get(context); if (contextServices != null) { contextServices.remove(serviceReg); } - // Remove the ServiceRegistration from the list of Services published by Class Name. + // Remove the ServiceRegistrationImpl from the list of Services published by Class Name. String[] clazzes = serviceReg.getClasses(); int size = clazzes.length; for (int i = 0; i < size; i++) { String clazz = clazzes[i]; - ArrayList services = (ArrayList) publishedServicesByClass.get(clazz); + List services = (List) publishedServicesByClass.get(clazz); services.remove(serviceReg); } - // Remove the ServiceRegistration from the list of all published Services. + // Remove the ServiceRegistrationImpl from the list of all published Services. allPublishedServices.remove(serviceReg); } /** - * Lookup Service References in the data structure by class name and filter. + * Lookup Service Registrations in the data structure by class name and filter. * * @param clazz The class name with which the service was registered or * <code>null</code> for all services. * @param filter The filter criteria. + * @return List<ServiceRegistrationImpl> */ /* @GuardedBy("this") */ - private List lookupServiceReferences(String clazz, Filter filter) { - ArrayList result; + private List lookupServiceRegistrations(String clazz, Filter filter) { + List result; if (clazz == null) { /* all services */ result = allPublishedServices; } else { /* services registered under the class name */ - result = (ArrayList) publishedServicesByClass.get(clazz); - if (result == null) { - return Collections.EMPTY_LIST; - } + result = (List) publishedServicesByClass.get(clazz); + } + + if ((result == null) || (result.size() == 0)) { + return Collections.EMPTY_LIST; } result = new ArrayList(result); /* make a new list since we don't want to change the real list */ + if (filter == null) { + return result; + } + ListIterator iter = result.listIterator(); while (iter.hasNext()) { ServiceRegistrationImpl registration = (ServiceRegistrationImpl) iter.next(); - ServiceReferenceImpl reference = registration.getReferenceImpl(); - if ((filter == null) || filter.match(reference)) { - iter.set(reference); /* replace the registration with its reference */ - } else { + if (!filter.match(registration.getReferenceImpl())) { iter.remove(); } } @@ -774,33 +759,30 @@ public class ServiceRegistry { * Lookup Service Registrations in the data structure by BundleContext. * * @param context The BundleContext for which to return Service Registrations. + * @return List<ServiceRegistrationImpl> */ /* @GuardedBy("this") */ private List lookupServiceRegistrations(BundleContextImpl context) { - ArrayList result = (ArrayList) publishedServicesByContext.get(context); + List result = (List) publishedServicesByContext.get(context); - if (result == null) { + if ((result == null) || (result.size() == 0)) { return Collections.EMPTY_LIST; } - result = new ArrayList(result); /* make a new list since we don't want to change the real list */ - return result; + return new ArrayList(result); /* make a new list since we don't want to change the real list */ } /** - * Lookup Service References in the data structure by BundleContext. + * Modify a List<ServiceRegistrationImpl> to a List<ServiceReferenceImpl>. * - * @param context The BundleContext for which to return Service References. + * @param result The input List<ServiceRegistrationImpl>. + * @return List<ServiceReferenceImpl> */ - /* @GuardedBy("this") */ - private List lookupServiceReferences(BundleContextImpl context) { - List result = lookupServiceRegistrations(context); - + private static List changeRegistrationsToReferences(List result) { ListIterator iter = result.listIterator(); while (iter.hasNext()) { ServiceRegistrationImpl registration = (ServiceRegistrationImpl) iter.next(); - ServiceReferenceImpl reference = registration.getReferenceImpl(); - iter.set(reference); /* replace the registration with its reference */ + iter.set(registration.getReferenceImpl()); /* replace the registration with its reference */ } return result; } @@ -923,4 +905,61 @@ public class ServiceRegistry { return false; return true; } + + private static final String findHookName = FindHook.class.getName(); + + /** + * Call the registered FindHook services to allow them to inspect and possibly shrink the result. + * The FindHook must be called in order: descending by service.ranking, then ascending by service.id. + * This is the natural order for ServiceReference. + * + * @param context The context of the bundle getting the service references. + * @param clazz The class name used to search for the service references. + * @param filterstring The filter used to search for the service references. + * @param allservices True if getAllServiceReferences called. + * @param result The result to return to the caller which may have been shrunk by the FindHooks. + */ + private void processFindHooks(BundleContextImpl context, String clazz, String filterstring, boolean allservices, Collection result) { + BundleContextImpl systemBundleContext = framework.getSystemBundleContext(); + if (systemBundleContext == null) { // if no system bundle context, we are done! + return; + } + + if (Debug.DEBUG && Debug.DEBUG_SERVICES) { + Debug.println("processFindHook(" + context.getBundleImpl() + "," + clazz + "," + filterstring + "," + allservices + "," + result + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + } + + Collection references = new ShrinkableCollection(result); // prevent hooks from adding to result + List hooks; + synchronized (this) { + hooks = lookupServiceRegistrations(findHookName, null); + } + // Since the list is already sorted, we don't need to sort the list to call the hooks + // in the proper order. + + Iterator iter = hooks.iterator(); + while (iter.hasNext()) { + ServiceRegistrationImpl registration = (ServiceRegistrationImpl) iter.next(); + Object findHook = registration.getService(systemBundleContext); + if (findHook == null) { // if the hook is null + continue; + } + try { + if (findHook instanceof FindHook) { // if the hook is usable + ((FindHook) findHook).find(context, clazz, filterstring, allservices, references); + } + } catch (Throwable t) { + if (Debug.DEBUG && Debug.DEBUG_SERVICES) { + Debug.println(findHook + ".find() exception: " + t.getMessage()); //$NON-NLS-1$ + Debug.printStackTrace(t); + } + // allow the adaptor to handle this unexpected error + framework.getAdaptor().handleRuntimeError(t); + ServiceException se = new ServiceException(NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, findHook.getClass().getName(), "find"), t); //$NON-NLS-1$ + framework.publishFrameworkEvent(FrameworkEvent.ERROR, registration.getBundle(), se); + } finally { + registration.ungetService(systemBundleContext); + } + } + } } diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceUse.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceUse.java index 8e297527f..b67baf9b4 100755 --- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceUse.java +++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ServiceUse.java @@ -56,7 +56,7 @@ public class ServiceUse { */ ServiceUse(BundleContextImpl context, ServiceRegistrationImpl registration) { this.useCount = 0; - Object service = registration.getService(); + Object service = registration.getServiceObject(); if (service instanceof ServiceFactory) { this.factory = (ServiceFactory) service; this.cachedService = null; diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ShrinkableCollection.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ShrinkableCollection.java new file mode 100644 index 000000000..cbc70ec7e --- /dev/null +++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/serviceregistry/ShrinkableCollection.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2008 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 + *******************************************************************************/ + +package org.eclipse.osgi.internal.serviceregistry; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A Shrinkable Collection. This class provides a wrapper for a collection + * that allows items to be removed from the wrapped collection (shrinking) but + * does not allow items to be added to the wrapped collection. + * + * <p> + * All the optional <code>Collection</code> operations except + * <code>add</code> and <code>addAll</code> are supported. Attempting to add to the + * collection will result in an <code>UnsupportedOperationException</code>. + * + */ + +public class ShrinkableCollection implements Collection { + private final Collection collection; + + ShrinkableCollection(Collection c) { + if (c == null) { + throw new NullPointerException(); + } + collection = c; + } + + public boolean add(Object var0) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection var0) { + throw new UnsupportedOperationException(); + } + + public void clear() { + collection.clear(); + } + + public boolean contains(Object var0) { + return collection.contains(var0); + } + + public boolean containsAll(Collection var0) { + return collection.containsAll(var0); + } + + public boolean isEmpty() { + return collection.isEmpty(); + } + + public Iterator iterator() { + return collection.iterator(); + } + + public boolean remove(Object var0) { + return collection.remove(var0); + } + + public boolean removeAll(Collection var0) { + return collection.removeAll(var0); + } + + public boolean retainAll(Collection var0) { + return collection.retainAll(var0); + } + + public int size() { + return collection.size(); + } + + public Object[] toArray() { + return collection.toArray(); + } + + public Object[] toArray(Object[] var0) { + return collection.toArray(var0); + } +} |