Bug 476145 - [region] performance issue with large number of bundle listeners and bundle events
diff --git a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/RegionReflectionUtils.java b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/RegionReflectionUtils.java
index 87a13f5..e33d2d2 100644
--- a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/RegionReflectionUtils.java
+++ b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/RegionReflectionUtils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 IBM Corporation and others.
+ * Copyright (c) 2013, 2015 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
@@ -172,10 +172,10 @@
 		return (CollisionHook) newInstance(regionBundleCollisionHook, classParams, constructorArgs);
 	}
 
-	public static EventHook newRegionBundleEventHook(RegionDigraph digraph, FindHook findHook, ThreadLocal<Region> threadLocal) {
+	public static EventHook newRegionBundleEventHook(RegionDigraph digraph, ThreadLocal<Region> threadLocal, long bundleId) {
 		Class<?> regionBundleEventHook = loadRegionImplClass(RegionBundleEventHook);
-		Class<?>[] classParams = new Class<?>[] {RegionDigraph.class, FindHook.class, ThreadLocal.class};
-		Object[] constructorArgs = new Object[] {digraph, findHook, threadLocal};
+		Class<?>[] classParams = new Class<?>[] {RegionDigraph.class, ThreadLocal.class, long.class};
+		Object[] constructorArgs = new Object[] {digraph, threadLocal, bundleId};
 		return (EventHook) newInstance(regionBundleEventHook, classParams, constructorArgs);
 	}
 
diff --git a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionBundleEventHookTests.java b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionBundleEventHookTests.java
index 38f0de4..a6047b2 100644
--- a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionBundleEventHookTests.java
+++ b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionBundleEventHookTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2013 VMware Inc.
+ * Copyright (c) 2011, 2015 VMware Inc. 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
@@ -11,76 +11,72 @@
 
 package org.eclipse.equinox.region.internal.tests.hook;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Collection;
-import java.util.HashSet;
-import org.easymock.EasyMock;
-import org.eclipse.equinox.region.Region;
-import org.eclipse.equinox.region.RegionDigraph;
+import java.util.*;
+import org.eclipse.equinox.region.*;
 import org.eclipse.equinox.region.internal.tests.RegionReflectionUtils;
 import org.eclipse.virgo.teststubs.osgi.framework.StubBundle;
 import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext;
 import org.junit.*;
 import org.osgi.framework.*;
 import org.osgi.framework.hooks.bundle.EventHook;
-import org.osgi.framework.hooks.bundle.FindHook;
 
 public class RegionBundleEventHookTests {
 
-	private BundleEvent bundleEvent;
+	private static final String BUNDLE_X = "X";
 
-	private Collection<BundleContext> contexts;
+	private static final Version BUNDLE_VERSION = new Version("0");
 
-	private Bundle eventBundle;
+	private long bundleId;
 
-	private RegionDigraph mockRegionDigraph;
+	private static final String REGION_A = "RegionA";
 
-	private ThreadLocal<Region> threadLocal;
+	private static final String BUNDLE_A = "BundleA";
+
+	private static final String REGION_B = "RegionB";
+
+	private static final String BUNDLE_B = "BundleB";
+
+	private static final String REGION_C = "RegionC";
+
+	private static final String BUNDLE_C = "BundleC";
+
+	private static final String REGION_D = "RegionD";
+
+	private static final String BUNDLE_D = "BundleD";
 
 	private RegionDigraph digraph;
 
-	private Region mockRegion1;
+	private EventHook bundleEventHook;
 
-	private Region mockRegion2;
+	private Map<String, Region> regions;
 
-	private BundleEvent installedEvent1;
+	private Map<String, Bundle> bundles;
 
-	private BundleEvent installedEvent2;
-
-	private StubBundle eventBundle1;
-
-	private StubBundle eventBundle2;
+	private ThreadLocal<Region> threadLocal;
 
 	@Before
 	public void setUp() throws Exception {
-		this.mockRegionDigraph = EasyMock.createMock(RegionDigraph.class);
-		this.eventBundle = new StubBundle();
-
-		this.eventBundle1 = new StubBundle(1L, "my.bundle1", new Version("0"), "loc1");
-		this.eventBundle2 = new StubBundle(2L, "my.bundle2", new Version("0"), "loc2");
-
-		this.bundleEvent = new BundleEvent(BundleEvent.STARTED, this.eventBundle, this.eventBundle);
-
-		this.installedEvent1 = new BundleEvent(BundleEvent.INSTALLED, eventBundle1, this.eventBundle);
-		this.installedEvent2 = new BundleEvent(BundleEvent.INSTALLED, eventBundle2, this.eventBundle);
-
-		this.contexts = new HashSet<BundleContext>();
-		StubBundleContext stubListenerBundleContext = new StubBundleContext();
-		this.contexts.add(stubListenerBundleContext);
-		this.threadLocal = new ThreadLocal<Region>();
+		this.bundleId = 1L;
+		this.regions = new HashMap<String, Region>();
+		this.bundles = new HashMap<String, Bundle>();
 
 		StubBundle stubSystemBundle = new StubBundle(0L, "osgi.framework", new Version("0"), "loc");
 		StubBundleContext stubBundleContext = new StubBundleContext();
 		stubBundleContext.addInstalledBundle(stubSystemBundle);
-		this.digraph = RegionReflectionUtils.newStandardRegionDigraph(stubBundleContext, new ThreadLocal<Region>());
-		this.digraph.createRegion("mockRegion1");
-		this.digraph.createRegion("mockRegion2");
-		this.mockRegion1 = digraph.getRegion("mockRegion1");
-		this.mockRegion2 = digraph.getRegion("mockRegion2");
-		this.mockRegion1.addBundle(this.eventBundle);
+		this.threadLocal = new ThreadLocal<Region>();
+		this.digraph = RegionReflectionUtils.newStandardRegionDigraph(stubBundleContext, this.threadLocal);
+		this.bundleEventHook = RegionReflectionUtils.newRegionBundleEventHook(digraph, threadLocal, stubSystemBundle.getBundleId());
 
+		// Create regions A, B, C, D containing bundles A, B, C, D, respectively.
+		createRegion(REGION_A, BUNDLE_A);
+		createRegion(REGION_B, BUNDLE_B);
+		createRegion(REGION_C, BUNDLE_C);
+		createRegion(REGION_D, BUNDLE_D);
+
+		createBundle(BUNDLE_X);
 	}
 
 	@After
@@ -89,53 +85,225 @@
 	}
 
 	@Test
-	public void testEventAllowed() {
-		FindHook mockFindHook = new FindHook() {
-
-			@Override
-			public void find(BundleContext context, Collection<Bundle> bundles) {
-				// nothing
-			}
-		};
-		EventHook eventHook = RegionReflectionUtils.newRegionBundleEventHook(this.mockRegionDigraph, mockFindHook, this.threadLocal);
-		eventHook.event(this.bundleEvent, this.contexts);
-		assertEquals(1, this.contexts.size());
+	public void testEventInSameRegion() {
+		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_A), contexts);
+		assertTrue(contexts.contains(bundleContext(BUNDLE_A)));
 	}
 
 	@Test
-	public void testEventNotAllowed() {
-		FindHook mockFindHook = new FindHook() {
+	public void testEventInDisconnectedRegion() {
+		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_B), contexts);
+		assertFalse(contexts.contains(bundleContext(BUNDLE_A)));
+	}
 
-			@Override
-			public void find(BundleContext context, Collection<Bundle> bundles) {
-				bundles.clear();
-			}
-		};
-		EventHook eventHook = RegionReflectionUtils.newRegionBundleEventHook(this.mockRegionDigraph, mockFindHook, this.threadLocal);
-		eventHook.event(this.bundleEvent, this.contexts);
-		assertTrue(this.contexts.isEmpty());
+	@Test
+	public void testEventConnectedRegionAllowed() throws BundleException, InvalidSyntaxException {
+		doTestEventConnectedRegionAllowed(false);
+	}
+
+	@Test
+	public void testEventConnectedRegionAllowedWithNegate() throws BundleException, InvalidSyntaxException {
+		doTestEventConnectedRegionAllowed(true);
+	}
+
+	private void doTestEventConnectedRegionAllowed(boolean negate) throws BundleException, InvalidSyntaxException {
+		RegionFilter filter = createFilter(negate, BUNDLE_B);
+		region(REGION_A).connectRegion(region(REGION_B), filter);
+
+		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_B), contexts);
+		assertTrue(contexts.contains(bundleContext(BUNDLE_A)));
+	}
+
+	@Test
+	public void testEventConnectedRegionFiltering() throws BundleException, InvalidSyntaxException {
+		doTestEventConnectedRegionFiltering(false);
+	}
+
+	@Test
+	public void testEventConnectedRegionFilteringWithNegate() throws BundleException, InvalidSyntaxException {
+		doTestEventConnectedRegionFiltering(true);
+	}
+
+	private void doTestEventConnectedRegionFiltering(boolean negate) throws BundleException, InvalidSyntaxException {
+		region(REGION_A).connectRegion(region(REGION_B), createFilter(negate, BUNDLE_B));
+		Bundle x = createBundle(BUNDLE_X);
+		region(REGION_B).addBundle(x);
+
+		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A, BUNDLE_B, BUNDLE_X);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_X), contexts);
+		assertTrue(contexts.contains(bundleContext(BUNDLE_B)));
+		assertTrue(contexts.contains(bundleContext(BUNDLE_X)));
+		assertFalse(contexts.contains(bundleContext(BUNDLE_A)));
+	}
+
+	@Test
+	public void testEventTransitive() throws BundleException, InvalidSyntaxException {
+		doTestEventTransitive(false);
+	}
+
+	@Test
+	public void testEventTransitiveWithNegate() throws BundleException, InvalidSyntaxException {
+		doTestEventTransitive(true);
+	}
+
+	private void doTestEventTransitive(boolean negate) throws BundleException, InvalidSyntaxException {
+		region(REGION_A).connectRegion(region(REGION_B), createFilter(negate, BUNDLE_C));
+		region(REGION_B).connectRegion(region(REGION_C), createFilter(negate, BUNDLE_C));
+		region(REGION_C).addBundle(bundle(BUNDLE_X));
+
+		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A, BUNDLE_B, BUNDLE_X);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_C), contexts);
+		assertTrue(contexts.contains(bundleContext(BUNDLE_B)));
+		assertTrue(contexts.contains(bundleContext(BUNDLE_X)));
+		assertTrue(contexts.contains(bundleContext(BUNDLE_A)));
+
+		contexts = bundleContexts(BUNDLE_A, BUNDLE_B, BUNDLE_X);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_X), contexts);
+		assertFalse(contexts.contains(bundleContext(BUNDLE_B)));
+		assertTrue(contexts.contains(bundleContext(BUNDLE_X)));
+		assertFalse(contexts.contains(bundleContext(BUNDLE_A)));
+	}
+
+	@Test
+	public void testEventInCyclicGraph() throws BundleException, InvalidSyntaxException {
+		doTestEventInCyclicGraph(false);
+	}
+
+	@Test
+	public void testEventInCyclicGraphWithNegate() throws BundleException, InvalidSyntaxException {
+
+		doTestEventInCyclicGraph(true);
+	}
+
+	private void doTestEventInCyclicGraph(boolean negate) throws BundleException, InvalidSyntaxException {
+		region(REGION_D).addBundle(bundle(BUNDLE_X));
+
+		region(REGION_A).connectRegion(region(REGION_B), createFilter(negate, BUNDLE_D, BUNDLE_X));
+		region(REGION_B).connectRegion(region(REGION_A), createFilter(negate));
+
+		region(REGION_B).connectRegion(region(REGION_D), createFilter(negate, BUNDLE_D));
+		region(REGION_D).connectRegion(region(REGION_B), createFilter(negate));
+
+		region(REGION_B).connectRegion(region(REGION_C), createFilter(negate, BUNDLE_X));
+		region(REGION_C).connectRegion(region(REGION_B), createFilter(negate));
+
+		region(REGION_C).connectRegion(region(REGION_D), createFilter(negate, BUNDLE_X));
+		region(REGION_D).connectRegion(region(REGION_C), createFilter(negate));
+
+		region(REGION_A).connectRegion(region(REGION_C), createFilter(negate));
+		region(REGION_C).connectRegion(region(REGION_A), createFilter(negate));
+
+		region(REGION_D).connectRegion(region(REGION_A), createFilter(negate));
+		region(REGION_A).connectRegion(region(REGION_D), createFilter(negate));
+
+		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A, BUNDLE_B);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_B), contexts);
+		assertFalse(contexts.contains(bundleContext(BUNDLE_A)));
+		assertTrue(contexts.contains(bundleContext(BUNDLE_B)));
+
+		contexts = bundleContexts(BUNDLE_A, BUNDLE_B);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_C), contexts);
+		assertFalse(contexts.contains(bundleContext(BUNDLE_A)));
+		assertFalse(contexts.contains(bundleContext(BUNDLE_B)));
+
+		contexts = bundleContexts(BUNDLE_A, BUNDLE_B);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_D), contexts);
+		assertTrue(contexts.contains(bundleContext(BUNDLE_A)));
+		assertTrue(contexts.contains(bundleContext(BUNDLE_B)));
+
+		contexts = bundleContexts(BUNDLE_A, BUNDLE_B);
+		this.bundleEventHook.event(bundleEvent(BUNDLE_X), contexts);
+		assertTrue(contexts.contains(bundleContext(BUNDLE_A)));
+		assertTrue(contexts.contains(bundleContext(BUNDLE_B)));
+	}
+
+	@Test
+	public void testEventFromSystemBundle() {
+		Bundle systemBundle = new StubBundle(0L, "sys", BUNDLE_VERSION, "");
+		Collection<BundleContext> contexts = new ArrayList<BundleContext>(Arrays.asList(systemBundle.getBundleContext()));
+		this.bundleEventHook.event(bundleEvent(BUNDLE_A), contexts);
+		assertTrue(contexts.contains(systemBundle.getBundleContext()));
+	}
+
+	@Test
+	public void testEventFromBundleInNoRegion() {
+		Bundle stranger = createBundle("stranger");
+		Collection<BundleContext> contexts = new ArrayList<BundleContext>(Arrays.asList(stranger.getBundleContext()));
+		this.bundleEventHook.event(bundleEvent(BUNDLE_A), contexts);
+		assertTrue(contexts.isEmpty());
 	}
 
 	@Test
 	public void testDefaultRegion() {
-		FindHook mockFindHook = new FindHook() {
-
-			@Override
-			public void find(BundleContext context, Collection<Bundle> bundles) {
-				bundles.clear();
-			}
-		};
-
 		this.digraph.setDefaultRegion(null);
-		EventHook eventHook = RegionReflectionUtils.newRegionBundleEventHook(this.digraph, mockFindHook, this.threadLocal);
-		eventHook.event(this.installedEvent1, this.contexts);
-		assertTrue(this.digraph.getRegion(this.eventBundle1).equals(this.mockRegion1));
+		Bundle x = createBundle("installed.X");
+		this.bundleEventHook.event(new BundleEvent(BundleEvent.INSTALLED, x, bundle(BUNDLE_A)), Collections.<BundleContext> emptyList());
+		assertTrue(this.digraph.getRegion(x).equals(region(REGION_A)));
 
-		this.digraph.setDefaultRegion(this.mockRegion2);
-		eventHook = RegionReflectionUtils.newRegionBundleEventHook(this.digraph, mockFindHook, this.threadLocal);
-		eventHook.event(this.installedEvent2, this.contexts);
-		assertTrue(this.digraph.getRegion(this.eventBundle2).equals(this.mockRegion2));
-
+		this.digraph.setDefaultRegion(region(REGION_B));
+		Bundle y = createBundle("installed.Y");
+		this.bundleEventHook.event(new BundleEvent(BundleEvent.INSTALLED, y, bundle(BUNDLE_A)), Collections.<BundleContext> emptyList());
+		assertTrue(this.digraph.getRegion(y).equals(region(REGION_B)));
 	}
 
+	private Region createRegion(String regionName, String... bundleSymbolicNames) throws BundleException {
+		Region region = this.digraph.createRegion(regionName);
+		for (String bundleSymbolicName : bundleSymbolicNames) {
+			Bundle stubBundle = createBundle(bundleSymbolicName);
+			region.addBundle(stubBundle);
+		}
+		this.regions.put(regionName, region);
+		return region;
+	}
+
+	private Region region(String regionName) {
+		return this.regions.get(regionName);
+	}
+
+	private RegionFilter createFilter(boolean negate, String... bundleSymbolicNames) throws InvalidSyntaxException {
+		Collection<String> filters = new ArrayList<String>(bundleSymbolicNames.length);
+		for (String bundleSymbolicName : bundleSymbolicNames) {
+			filters.add('(' + RegionFilter.VISIBLE_BUNDLE_NAMESPACE + '=' + bundleSymbolicName + ')');
+		}
+		RegionFilterBuilder builder = digraph.createRegionFilterBuilder();
+		for (String filter : filters) {
+			builder.allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, filter);
+		}
+
+		if (negate) {
+			String negateFilter = "(!(|" + "(" + RegionFilter.VISIBLE_ALL_NAMESPACE_ATTRIBUTE + "=" + RegionFilter.VISIBLE_BUNDLE_NAMESPACE + ")" + "(" + RegionFilter.VISIBLE_ALL_NAMESPACE_ATTRIBUTE + "=" + RegionFilter.VISIBLE_BUNDLE_LIFECYCLE_NAMESPACE + ")" + "))";
+			builder.allow(RegionFilter.VISIBLE_ALL_NAMESPACE, negateFilter);
+		}
+		return builder.build();
+	}
+
+	private Bundle createBundle(String bundleSymbolicName) {
+		Bundle stubBundle = new StubBundle(this.bundleId++, bundleSymbolicName, BUNDLE_VERSION, "loc:" + bundleSymbolicName);
+		this.bundles.put(bundleSymbolicName, stubBundle);
+		return stubBundle;
+	}
+
+	private Collection<BundleContext> bundleContexts(String... bundleSymbolicNames) {
+		Collection<BundleContext> contexts = new ArrayList<BundleContext>();
+		for (String symbolicName : bundleSymbolicNames) {
+			contexts.add(bundleContext(symbolicName));
+		}
+		return contexts;
+	}
+
+	private BundleContext bundleContext(String bundleSymbolicName) {
+		return bundle(bundleSymbolicName).getBundleContext();
+	}
+
+	private BundleEvent bundleEvent(String budnleSymbolicName) {
+		return new BundleEvent(BundleEvent.STARTED, bundle(budnleSymbolicName));
+	}
+
+	private Bundle bundle(String bundleSymbolicName) {
+		Bundle bundleA = this.bundles.get(bundleSymbolicName);
+		return bundleA;
+	}
 }
diff --git a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionServiceEventHookTests.java b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionServiceEventHookTests.java
index cb0758b..014370f 100644
--- a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionServiceEventHookTests.java
+++ b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/region/internal/tests/hook/RegionServiceEventHookTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2015 VMware Inc.
+ * Copyright (c) 2011, 2015 VMware Inc. 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
@@ -94,21 +94,21 @@
 	}
 
 	@Test
-	public void testFindInSameRegion() {
+	public void testEventInSameRegion() {
 		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A);
 		this.serviceEventHook.event(serviceEvent(BUNDLE_A), contexts);
 		assertTrue(contexts.contains(bundleContext(BUNDLE_A)));
 	}
 
 	@Test
-	public void testFindInDisconnectedRegion() {
+	public void testEventInDisconnectedRegion() {
 		Collection<BundleContext> contexts = bundleContexts(BUNDLE_A);
 		this.serviceEventHook.event(serviceEvent(BUNDLE_B), contexts);
 		assertFalse(contexts.contains(bundleContext(BUNDLE_A)));
 	}
 
 	@Test
-	public void testFindConnectedRegionAllowed() throws BundleException, InvalidSyntaxException {
+	public void testEventConnectedRegionAllowed() throws BundleException, InvalidSyntaxException {
 		RegionFilter filter = createFilter(BUNDLE_B);
 		region(REGION_A).connectRegion(region(REGION_B), filter);
 
@@ -118,7 +118,7 @@
 	}
 
 	@Test
-	public void testFindConnectedRegionFiltering() throws BundleException, InvalidSyntaxException {
+	public void testEventConnectedRegionFiltering() throws BundleException, InvalidSyntaxException {
 		region(REGION_A).connectRegion(region(REGION_B), createFilter(BUNDLE_B));
 		Bundle x = createBundle(BUNDLE_X);
 		region(REGION_B).addBundle(x);
@@ -131,7 +131,7 @@
 	}
 
 	@Test
-	public void testFindTransitive() throws BundleException, InvalidSyntaxException {
+	public void testEventTransitive() throws BundleException, InvalidSyntaxException {
 		region(REGION_A).connectRegion(region(REGION_B), createFilter(BUNDLE_C));
 		region(REGION_B).connectRegion(region(REGION_C), createFilter(BUNDLE_C));
 		region(REGION_C).addBundle(bundle(BUNDLE_X));
@@ -147,11 +147,10 @@
 		assertFalse(contexts.contains(bundleContext(BUNDLE_B)));
 		assertTrue(contexts.contains(bundleContext(BUNDLE_X)));
 		assertFalse(contexts.contains(bundleContext(BUNDLE_A)));
-
 	}
 
 	@Test
-	public void testFindTransitiveDups() throws BundleException, InvalidSyntaxException {
+	public void testEventTransitiveDups() throws BundleException, InvalidSyntaxException {
 		region(REGION_A).connectRegion(region(REGION_B), createFilter(BUNDLE_C));
 		region(REGION_A).connectRegion(region(REGION_C), createFilter(DUPLICATE_FIlTER));
 		region(REGION_A).connectRegion(region(REGION_D), createFilter(DUPLICATE_FIlTER));
@@ -177,7 +176,7 @@
 	}
 
 	@Test
-	public void testFindInCyclicGraph() throws BundleException, InvalidSyntaxException {
+	public void testEventInCyclicGraph() throws BundleException, InvalidSyntaxException {
 		region(REGION_D).addBundle(bundle(BUNDLE_X));
 
 		region(REGION_A).connectRegion(region(REGION_B), createFilter(BUNDLE_D, BUNDLE_X));
@@ -220,7 +219,7 @@
 	}
 
 	@Test
-	public void testFindFromSystemBundle() {
+	public void testEventFromSystemBundle() {
 		Bundle systemBundle = new StubBundle(0L, "sys", BUNDLE_VERSION, "");
 		Collection<BundleContext> contexts = new ArrayList<BundleContext>(Arrays.asList(systemBundle.getBundleContext()));
 		this.serviceEventHook.event(serviceEvent(BUNDLE_A), contexts);
@@ -228,7 +227,7 @@
 	}
 
 	@Test
-	public void testFindFromBundleInNoRegion() {
+	public void testEventFromBundleInNoRegion() {
 		Bundle stranger = createBundle("stranger");
 		Collection<BundleContext> contexts = new ArrayList<BundleContext>(Arrays.asList(stranger.getBundleContext()));
 		this.serviceEventHook.event(serviceEvent(BUNDLE_A), contexts);
diff --git a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/StandardRegionDigraph.java b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/StandardRegionDigraph.java
index e592370..fedb941 100644
--- a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/StandardRegionDigraph.java
+++ b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/StandardRegionDigraph.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2014 VMware Inc.
+ * Copyright (c) 2011, 2015 VMware Inc. 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,7 +87,7 @@
 		this.resolverHookFactory = new RegionResolverHookFactory(this);
 
 		this.bundleFindHook = new RegionBundleFindHook(this, bundleContext == null ? 0 : bundleContext.getBundle().getBundleId());
-		this.bundleEventHook = new RegionBundleEventHook(this, this.bundleFindHook, this.threadLocal);
+		this.bundleEventHook = new RegionBundleEventHook(this, this.threadLocal, bundleContext == null ? 0 : bundleContext.getBundle().getBundleId());
 		Object hook;
 		try {
 			hook = new RegionBundleCollisionHook(this, this.threadLocal);
diff --git a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleEventHook.java b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleEventHook.java
index 3edb511..8b27915 100644
--- a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleEventHook.java
+++ b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleEventHook.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011 VMware Inc.
+ * Copyright (c) 2011, 2015 VMware Inc. 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
@@ -16,7 +16,6 @@
 import org.eclipse.equinox.region.RegionDigraph;
 import org.osgi.framework.*;
 import org.osgi.framework.hooks.bundle.EventHook;
-import org.osgi.framework.hooks.bundle.FindHook;
 
 /**
  * {@link RegionBundleEventHook} manages the visibility of bundle events across regions according to the
@@ -33,14 +32,14 @@
 
 	private final RegionDigraph regionDigraph;
 
-	private final FindHook bundleFindHook;
-
 	private final ThreadLocal<Region> threadLocal;
 
-	public RegionBundleEventHook(RegionDigraph regionDigraph, FindHook bundleFindBook, ThreadLocal<Region> threadLocal) {
+	private final long hookImplID;
+
+	public RegionBundleEventHook(RegionDigraph regionDigraph, ThreadLocal<Region> threadLocal, long hookImplID) {
 		this.regionDigraph = regionDigraph;
-		this.bundleFindHook = bundleFindBook;
 		this.threadLocal = threadLocal;
+		this.hookImplID = hookImplID;
 	}
 
 	/**
@@ -52,10 +51,35 @@
 		if (event.getType() == BundleEvent.INSTALLED) {
 			bundleInstalled(eventBundle, event.getOrigin());
 		}
+		Map<Region, Boolean> regionAccess = new HashMap<Region, Boolean>();
 		Iterator<BundleContext> i = contexts.iterator();
 		while (i.hasNext()) {
-			if (!find(i.next(), eventBundle)) {
+			Bundle bundle = RegionBundleFindHook.getBundle(i.next());
+			if (bundle == null) {
+				// no bundle for context remove access from it
 				i.remove();
+				continue;
+			}
+
+			long bundleID = bundle.getBundleId();
+			if (bundleID == 0 || bundleID == hookImplID) {
+				// The system bundle and the hook impl bundle can see all bundles
+				continue;
+			}
+			Region region = regionDigraph.getRegion(bundle);
+			if (region == null) {
+				// no region for context remove access from it
+				i.remove();
+			} else {
+				Boolean accessible = regionAccess.get(region);
+				if (accessible == null) {
+					// we have not checked this region's access do it now
+					accessible = isAccessible(region, eventBundle);
+					regionAccess.put(region, accessible);
+				}
+				if (!accessible) {
+					i.remove();
+				}
 			}
 		}
 		if (event.getType() == BundleEvent.UNINSTALLED) {
@@ -63,10 +87,10 @@
 		}
 	}
 
-	private boolean find(BundleContext finderBundleContext, Bundle candidateBundle) {
+	private boolean isAccessible(Region region, Bundle candidateBundle) {
 		Collection<Bundle> candidates = new ArrayList<Bundle>(1);
 		candidates.add(candidateBundle);
-		this.bundleFindHook.find(finderBundleContext, candidates);
+		RegionBundleFindHook.find(region, candidates);
 		return !candidates.isEmpty();
 	}
 
diff --git a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleFindHook.java b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleFindHook.java
index 6c41f73..9cba364 100644
--- a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleFindHook.java
+++ b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/hook/RegionBundleFindHook.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 VMware Inc.
+ * Copyright (c) 2013, 2015 VMware Inc. 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
@@ -53,6 +53,10 @@
 		}
 
 		Region finderRegion = this.regionDigraph.getRegion(finderBundle);
+		RegionBundleFindHook.find(finderRegion, bundles);
+	}
+
+	static void find(Region finderRegion, Collection<Bundle> bundles) {
 		if (finderRegion == null) {
 			bundles.clear();
 			return;
@@ -65,7 +69,7 @@
 		bundles.retainAll(allowed);
 	}
 
-	class Visitor extends RegionDigraphVisitorBase<Bundle> {
+	static class Visitor extends RegionDigraphVisitorBase<Bundle> {
 
 		Visitor(Collection<Bundle> candidates) {
 			super(candidates);