bug 361905: encapsulate bundle id to region mapping
diff --git a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/internal/region/BundleIdBasedRegionTests.java b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/internal/region/BundleIdBasedRegionTests.java
index 804aa7e..76171ef 100644
--- a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/internal/region/BundleIdBasedRegionTests.java
+++ b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/internal/region/BundleIdBasedRegionTests.java
@@ -16,8 +16,8 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.HashSet;
+import java.util.Iterator;
import org.easymock.EasyMock;
import org.eclipse.equinox.region.*;
import org.eclipse.equinox.region.RegionDigraph.FilteredRegion;
@@ -57,13 +57,11 @@
RegionFilter mockRegionFilter;
private ThreadLocal<Region> threadLocal;
- private Object globalUpdateMonitor = new Object();
- private Map<Long, Region> globalBundleToRegion = new HashMap<Long, Region>();
- private AtomicLong globalTimeStamp = new AtomicLong();
+
+ private BundleIdToRegionMapping bundleIdToRegionMapping;
@Before
public void setUp() throws Exception {
- this.globalBundleToRegion.clear();
this.threadLocal = new ThreadLocal<Region>();
this.mockBundle = EasyMock.createMock(Bundle.class);
EasyMock.expect(this.mockBundle.getSymbolicName()).andReturn(BUNDLE_SYMBOLIC_NAME).anyTimes();
@@ -98,6 +96,7 @@
this.mockGraph = EasyMock.createMock(RegionDigraph.class);
this.mockGraph.connect(EasyMock.isA(Region.class), EasyMock.eq(this.mockRegionFilter), EasyMock.eq(this.mockRegion));
EasyMock.expectLastCall().anyTimes();
+ this.bundleIdToRegionMapping = new StandardBundleIdToRegionMapping();
}
private void replayMocks() {
@@ -113,10 +112,18 @@
public void testGetName() {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
assertEquals(REGION_NAME, r.getName());
}
+ private BundleIdBasedRegion createDefaultBundleIdBasedRegion() {
+ return createBundleIdBasedRegion(REGION_NAME);
+ }
+
+ private BundleIdBasedRegion createBundleIdBasedRegion(String regionName) {
+ return new BundleIdBasedRegion(regionName, this.mockGraph, this.bundleIdToRegionMapping, this.mockBundleContext, this.threadLocal);
+ }
+
private void defaultSetUp() {
EasyMock.expect(this.mockGraph.iterator()).andReturn(this.regionIterator).anyTimes();
EasyMock.expect(this.mockGraph.getEdges(EasyMock.isA(Region.class))).andReturn(new HashSet<FilteredRegion>()).anyTimes();
@@ -144,7 +151,7 @@
EasyMock.expect(this.mockGraph.getEdges(EasyMock.isA(Region.class))).andReturn(edges).anyTimes();
replayMocks();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.addBundle(this.mockBundle);
}
@@ -152,7 +159,7 @@
public void testAddExistingBundle() throws BundleException {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.addBundle(this.mockBundle);
r.addBundle(this.mockBundle);
}
@@ -167,7 +174,7 @@
EasyMock.expect(mockBundle2.getBundleId()).andReturn(BUNDLE_ID_2).anyTimes();
EasyMock.replay(mockBundle2);
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.addBundle(this.mockBundle);
r.addBundle(mockBundle2);
}
@@ -184,7 +191,7 @@
r.addBundle(this.mockBundle.getBundleId());
}
- private Region regionForBundlePersentInAnotherRegionTest() {
+ private Region regionForBundlePersentInAnotherRegionTest() throws BundleException {
this.regionIterator = new Iterator<Region>() {
private int next = 2;
@@ -213,11 +220,11 @@
EasyMock.expect(this.mockGraph.getEdges(EasyMock.isA(Region.class))).andReturn(new HashSet<FilteredRegion>()).anyTimes();
EasyMock.expect(this.mockRegion.contains(EasyMock.eq(BUNDLE_ID))).andReturn(true).anyTimes();
EasyMock.expect(this.mockRegion2.contains(EasyMock.eq(BUNDLE_ID))).andReturn(false).anyTimes();
- this.globalBundleToRegion.put(BUNDLE_ID, mockRegion);
+ this.bundleIdToRegionMapping.associateBundleWithRegion(BUNDLE_ID, mockRegion);
replayMocks();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
return r;
}
@@ -239,7 +246,7 @@
public void testContains() throws BundleException {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.addBundle(this.mockBundle);
assertTrue(r.contains(this.mockBundle));
}
@@ -248,7 +255,7 @@
public void testDoesNotContain() {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
assertFalse(r.contains(this.mockBundle));
}
@@ -256,7 +263,7 @@
public void testGetBundle() throws BundleException {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.addBundle(this.mockBundle);
assertEquals(this.mockBundle, r.getBundle(BUNDLE_SYMBOLIC_NAME, BUNDLE_VERSION));
}
@@ -265,7 +272,7 @@
public void testGetBundleNotFound() throws BundleException {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.addBundle(this.mockBundle);
assertNull(r.getBundle(BUNDLE_SYMBOLIC_NAME_2, BUNDLE_VERSION));
}
@@ -274,7 +281,7 @@
public void testConnectRegion() throws BundleException {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.connectRegion(this.mockRegion, this.mockRegionFilter);
}
@@ -282,8 +289,8 @@
public void testEquals() {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
- Region s = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
+ Region s = createDefaultBundleIdBasedRegion();
assertEquals(r, r);
assertEquals(r, s);
assertEquals(r.hashCode(), s.hashCode());
@@ -293,8 +300,8 @@
public void testNotEqual() {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
- Region s = new BundleIdBasedRegion(OTHER_REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
+ Region s = createBundleIdBasedRegion(OTHER_REGION_NAME);
assertFalse(r.equals(s));
assertFalse(r.equals(null));
}
@@ -302,7 +309,7 @@
@Test
public void testAddRemoveBundleId() throws BundleException {
defaultSetUp();
- Region r = new BundleIdBasedRegion(REGION_NAME, this.mockGraph, this.mockBundleContext, this.threadLocal, this.globalUpdateMonitor, this.globalTimeStamp, this.globalBundleToRegion);
+ Region r = createDefaultBundleIdBasedRegion();
r.addBundle(TEST_BUNDLE_ID);
assertTrue(r.contains(TEST_BUNDLE_ID));
r.removeBundle(TEST_BUNDLE_ID);
diff --git a/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/internal/region/StandardBundleIdToRegionMappingTests.java b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/internal/region/StandardBundleIdToRegionMappingTests.java
new file mode 100644
index 0000000..04bdcc2
--- /dev/null
+++ b/bundles/org.eclipse.equinox.region.tests/src/org/eclipse/equinox/internal/region/StandardBundleIdToRegionMappingTests.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2011 VMware Inc.
+ * 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:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.region;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.easymock.EasyMock;
+import org.eclipse.equinox.region.Region;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleException;
+
+public class StandardBundleIdToRegionMappingTests {
+
+ private BundleIdToRegionMapping bundleIdToRegionMapping;
+
+ private Region mockRegion;
+
+ private static final long TEST_BUNDLE_ID = 1L;
+
+ private static final long OTHER_TEST_BUNDLE_ID = 2L;
+
+ @Before
+ public void setUp() throws Exception {
+ this.bundleIdToRegionMapping = new StandardBundleIdToRegionMapping();
+ this.mockRegion = EasyMock.createMock(Region.class);
+ }
+
+ @Test
+ public void testAssociateBundleWithRegion() throws BundleException {
+ assertNull(this.bundleIdToRegionMapping.getRegion(TEST_BUNDLE_ID));
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ assertEquals(mockRegion, this.bundleIdToRegionMapping.getRegion(TEST_BUNDLE_ID));
+ }
+
+ @Test
+ public void testAssociateBundleAlreadyAssociatedWithRegion() throws BundleException {
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ }
+
+ @Test(expected = BundleException.class)
+ public void testAssociateBundleAlreadyAssociatedWithOtherRegion() throws BundleException {
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, EasyMock.createMock(Region.class));
+ }
+
+ @Test
+ public void testDissociateBundle() throws BundleException {
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ this.bundleIdToRegionMapping.dissociateBundle(TEST_BUNDLE_ID);
+ assertNull(this.bundleIdToRegionMapping.getRegion(TEST_BUNDLE_ID));
+ }
+
+ @Test
+ public void testIsBundleAssociatedWithRegion() throws BundleException {
+ assertFalse(this.bundleIdToRegionMapping.isBundleAssociatedWithRegion(TEST_BUNDLE_ID, mockRegion));
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ assertTrue(this.bundleIdToRegionMapping.isBundleAssociatedWithRegion(TEST_BUNDLE_ID, mockRegion));
+ }
+
+ @Test
+ public void testGetBundleIds() throws BundleException {
+ assertEquals(0, this.bundleIdToRegionMapping.getBundleIds(mockRegion).size());
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ this.bundleIdToRegionMapping.associateBundleWithRegion(OTHER_TEST_BUNDLE_ID, mockRegion);
+ assertEquals(2, this.bundleIdToRegionMapping.getBundleIds(mockRegion).size());
+ assertTrue(this.bundleIdToRegionMapping.getBundleIds(mockRegion).contains(TEST_BUNDLE_ID));
+ assertTrue(this.bundleIdToRegionMapping.getBundleIds(mockRegion).contains(OTHER_TEST_BUNDLE_ID));
+ }
+
+ @Test
+ public void testClear() throws BundleException {
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ this.bundleIdToRegionMapping.clear();
+ assertNull(this.bundleIdToRegionMapping.getRegion(TEST_BUNDLE_ID));
+ }
+
+ @Test
+ public void testGetRegion() throws BundleException {
+ assertNull(this.bundleIdToRegionMapping.getRegion(TEST_BUNDLE_ID));
+ this.bundleIdToRegionMapping.associateBundleWithRegion(TEST_BUNDLE_ID, mockRegion);
+ this.bundleIdToRegionMapping.associateBundleWithRegion(OTHER_TEST_BUNDLE_ID, mockRegion);
+ assertEquals(mockRegion, this.bundleIdToRegionMapping.getRegion(TEST_BUNDLE_ID));
+ assertEquals(mockRegion, this.bundleIdToRegionMapping.getRegion(OTHER_TEST_BUNDLE_ID));
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/BundleIdBasedRegion.java b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/BundleIdBasedRegion.java
index 5904537..d6a3670 100644
--- a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/BundleIdBasedRegion.java
+++ b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/BundleIdBasedRegion.java
@@ -15,8 +15,7 @@
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.Set;
import org.eclipse.equinox.region.*;
import org.eclipse.equinox.region.RegionDigraph.FilteredRegion;
import org.osgi.framework.*;
@@ -35,43 +34,34 @@
private static final String FILE_SCHEME = "file:"; //$NON-NLS-1$
- // Note that this global digraph monitor locks modifications and read operations on the RegionDigraph
- // This includes modifying and reading the bunlde ids included in this region
- // It should be considered a global lock on the complete digraph.
- private final Object globalUpdateMonitor;
- private final AtomicLong globalTimeStamp;
- //TODO: avoid sharing this map across classes.
- private final Map<Long, Region> globalBundleToRegion;
-
private final String regionName;
private final RegionDigraph regionDigraph;
+ private final BundleIdToRegionMapping bundleIdToRegionMapping;
+
private final BundleContext bundleContext;
private final ThreadLocal<Region> threadLocal;
- BundleIdBasedRegion(String regionName, RegionDigraph regionDigraph, BundleContext bundleContext, ThreadLocal<Region> threadLocal, Object globalUpdateMonitor, AtomicLong globalTimeStamp, Map<Long, Region> globalBundleToRegion) {
+ BundleIdBasedRegion(String regionName, RegionDigraph regionDigraph, BundleIdToRegionMapping bundleIdToRegionMapping, BundleContext bundleContext, ThreadLocal<Region> threadLocal) {
if (regionName == null)
throw new IllegalArgumentException("The region name must not be null"); //$NON-NLS-1$
if (regionDigraph == null)
throw new IllegalArgumentException("The region digraph must not be null"); //$NON-NLS-1$
- if (globalUpdateMonitor == null)
- throw new IllegalArgumentException("The global update monitor must not be null"); //$NON-NLS-1$
- if (globalBundleToRegion == null)
- throw new IllegalArgumentException("The global bundle to region must not be null"); //$NON-NLS-1$
+ if (bundleIdToRegionMapping == null)
+ throw new IllegalArgumentException("The bundle id to region mapping must not be null"); //$NON-NLS-1$
this.regionName = regionName;
this.regionDigraph = regionDigraph;
+ this.bundleIdToRegionMapping = bundleIdToRegionMapping;
this.bundleContext = bundleContext;
this.threadLocal = threadLocal;
- this.globalUpdateMonitor = globalUpdateMonitor;
- this.globalTimeStamp = globalTimeStamp;
- this.globalBundleToRegion = globalBundleToRegion;
}
/**
* {@inheritDoc}
*/
+ @Override
public String getName() {
return this.regionName;
}
@@ -79,6 +69,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public void addBundle(Bundle bundle) throws BundleException {
addBundle(bundle.getBundleId());
}
@@ -86,22 +77,15 @@
/**
* {@inheritDoc}
*/
- // There is a global lock obtained to ensure consistency across the complete digraph
+ @Override
public void addBundle(long bundleId) throws BundleException {
- synchronized (this.globalUpdateMonitor) {
- Region r = this.globalBundleToRegion.get(bundleId);
- if (r != null && r != this) {
- throw new BundleException("Bundle '" + bundleId + "' is already associated with region '" + r + "'", BundleException.INVALID_OPERATION); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
- this.globalBundleToRegion.put(bundleId, this);
- this.globalTimeStamp.incrementAndGet();
- }
-
+ this.bundleIdToRegionMapping.associateBundleWithRegion(bundleId, this);
}
/**
* {@inheritDoc}
*/
+ @Override
public Bundle installBundle(String location, InputStream input) throws BundleException {
if (this.bundleContext == null)
throw new BundleException("This region is not connected to an OSGi Framework.", BundleException.INVALID_OPERATION); //$NON-NLS-1$
@@ -129,6 +113,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public Bundle installBundle(String location) throws BundleException {
return installBundle(location, null);
}
@@ -146,6 +131,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public Bundle getBundle(String symbolicName, Version version) {
if (bundleContext == null)
return null; // this region is not connected to an OSGi framework
@@ -163,6 +149,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public void connectRegion(Region headRegion, RegionFilter filter) throws BundleException {
this.regionDigraph.connect(this, filter, headRegion);
}
@@ -170,19 +157,20 @@
/**
* {@inheritDoc}
*/
+ @Override
public boolean contains(long bundleId) {
- synchronized (globalUpdateMonitor) {
- return globalBundleToRegion.get(bundleId) == this;
- }
+ return this.bundleIdToRegionMapping.isBundleAssociatedWithRegion(bundleId, this);
}
/**
* {@inheritDoc}
*/
+ @Override
public boolean contains(Bundle bundle) {
return contains(bundle.getBundleId());
}
+ @Override
public int hashCode() {
final int prime = 31;
int result = 1;
@@ -190,6 +178,7 @@
return result;
}
+ @Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
@@ -207,6 +196,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public void removeBundle(Bundle bundle) {
removeBundle(bundle.getBundleId());
@@ -215,11 +205,9 @@
/**
* {@inheritDoc}
*/
+ @Override
public void removeBundle(long bundleId) {
- synchronized (this.globalUpdateMonitor) {
- this.globalBundleToRegion.remove(bundleId);
- this.globalTimeStamp.incrementAndGet();
- }
+ this.bundleIdToRegionMapping.dissociateBundle(bundleId);
}
/**
@@ -230,22 +218,26 @@
return getName();
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public Set<Long> getBundleIds() {
- Set<Long> bundleIds = new HashSet<Long>();
- synchronized (this.globalUpdateMonitor) {
- for (Map.Entry<Long, Region> entry : globalBundleToRegion.entrySet()) {
- if (entry.getValue() == this) {
- bundleIds.add(entry.getKey());
- }
- }
- }
- return bundleIds;
+ return this.bundleIdToRegionMapping.getBundleIds(this);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public Set<FilteredRegion> getEdges() {
return this.regionDigraph.getEdges(this);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void visitSubgraph(RegionDigraphVisitor visitor) {
this.regionDigraph.visitSubgraph(this, visitor);
}
diff --git a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/BundleIdToRegionMapping.java b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/BundleIdToRegionMapping.java
new file mode 100644
index 0000000..865e71e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/BundleIdToRegionMapping.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2011 VMware Inc.
+ * 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:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.region;
+
+import java.util.Set;
+import org.eclipse.equinox.region.Region;
+import org.osgi.framework.BundleException;
+
+/**
+ * This internal interface is used to track which bundles belong to which regions.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Implementations must be thread safe.
+ */
+public interface BundleIdToRegionMapping {
+
+ /**
+ * Associates the given bundle id with the given region. If the bundle
+ * id is already associated with a different region, throws {@link BundleException}.
+ * If the bundle id is already associated with the given region, there is no
+ * effect on the association and no exception is thrown.
+ *
+ * TODO: need to check type of Region? Validity of region?
+ *
+ * @param bundleId the bundle id to be associated
+ * @param region the {@link Region} with which the bundle id is to be associated
+ * @throws BundleException
+ */
+ void associateBundleWithRegion(long bundleId, Region region) throws BundleException;
+
+ /**
+ * Dissociates the given bundle id with any region with which it may be
+ * associated.
+ *
+ * @param bundleId the bundle id to be dissociated
+ */
+ void dissociateBundle(long bundleId);
+
+ /**
+ * Returns the {@link Region} associated with the given bundle id or <code>null</code>
+ * if the given bundle id is not associated with a region.
+ *
+ * @param bundleId the bundle id whose region is required
+ * @return the {@link Region} associated with the given bundle id or or <code>null</code>
+ * if the given bundle id is not associated with a region
+ */
+ Region getRegion(long bundleId);
+
+ /**
+ * Checks the association of the given bundle id with the given region and returns
+ * <code>true</code> if and only if the given bundle id is associated with
+ * the given region
+ *
+ * @param bundleId the bundle id to be checked
+ * @param region the {@link Region} to be checked
+ * @return <code>true</code> if and only if the given bundle id is associated with
+ * the given region
+ */
+ boolean isBundleAssociatedWithRegion(long bundleId, Region region);
+
+ /**
+ * Returns a set of bundle ids associated with the given region. Never
+ * returns <code>null</code>.
+ *
+ * @param region the {@link Region} whose bundle ids are required
+ * @return the {@link Set} of bundle ids associated with the given region
+ */
+ Set<Long> getBundleIds(Region region);
+
+ /**
+ * Dissociates all bundle ids and regions.
+ */
+ void clear();
+
+}
diff --git a/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/StandardBundleIdToRegionMapping.java b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/StandardBundleIdToRegionMapping.java
new file mode 100644
index 0000000..9210535
--- /dev/null
+++ b/bundles/org.eclipse.equinox.region/src/org/eclipse/equinox/internal/region/StandardBundleIdToRegionMapping.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2011 VMware Inc.
+ * 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:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.region;
+
+import java.util.*;
+import org.eclipse.equinox.region.Region;
+import org.osgi.framework.BundleException;
+
+final class StandardBundleIdToRegionMapping implements BundleIdToRegionMapping {
+
+ private final Object monitor = new Object();
+
+ /*
+ * bundleToRegion maps a given bundle id to the region for which it belongs.
+ * this is a global map for all regions in the digraph
+ */
+ private final Map<Long, Region> bundleToRegion = new HashMap<Long, Region>();
+
+ /**
+ * {@inheritDoc}
+ **/
+ @Override
+ public void associateBundleWithRegion(long bundleId, Region region) throws BundleException {
+ synchronized (this.monitor) {
+ Region r = this.bundleToRegion.get(bundleId);
+ if (r != null && r != region) {
+ throw new BundleException("Bundle '" + bundleId + "' is already associated with region '" + r + "'", BundleException.INVALID_OPERATION); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ this.bundleToRegion.put(bundleId, region);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dissociateBundle(long bundleId) {
+ synchronized (this.monitor) {
+ this.bundleToRegion.remove(bundleId);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isBundleAssociatedWithRegion(long bundleId, Region region) {
+ synchronized (this.monitor) {
+ return this.bundleToRegion.get(bundleId) == region;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Long> getBundleIds(Region region) {
+ Set<Long> bundleIds = new HashSet<Long>();
+ synchronized (this.monitor) {
+ for (Map.Entry<Long, Region> entry : this.bundleToRegion.entrySet()) {
+ if (entry.getValue() == region) {
+ bundleIds.add(entry.getKey());
+ }
+ }
+ }
+ return Collections.unmodifiableSet(bundleIds);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ synchronized (this.monitor) {
+ this.bundleToRegion.clear();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Region getRegion(long bundleId) {
+ synchronized (this.monitor) {
+ return this.bundleToRegion.get(bundleId);
+ }
+ }
+}
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 06b7fc9..4837dd8 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
@@ -29,7 +29,7 @@
* Thread safe.
*
*/
-public final class StandardRegionDigraph implements RegionDigraph {
+public final class StandardRegionDigraph implements BundleIdToRegionMapping, RegionDigraph {
private static final Set<FilteredRegion> EMPTY_EDGE_SET = Collections.unmodifiableSet(new HashSet<FilteredRegion>());
@@ -39,11 +39,7 @@
private final Set<Region> regions = new HashSet<Region>();
- /*
- * bundleToRegion maps a given bundle id to the region for which it belongs.
- * this is a global map for all regions in the digraph
- */
- private final Map<Long, Region> bundleToRegion = new HashMap<Long, Region>();
+ private final BundleIdToRegionMapping bundleIdToRegionMapping;
/* edges maps a given region to an immutable set of edges with their tail at the given region. To update
* the edges for a region, the corresponding immutable set is replaced atomically. */
@@ -64,8 +60,8 @@
private final ResolverHookFactory resolverHookFactory;
private final StandardRegionDigraph origin;
// Guarded by the origin monitor
- private long originTimeStamp;
- private final AtomicLong timeStamp = new AtomicLong();
+ private long originUpdateCount;
+ private final AtomicLong updateCount = new AtomicLong();
private volatile Region defaultRegion;
@@ -80,6 +76,7 @@
private StandardRegionDigraph(BundleContext bundleContext, ThreadLocal<Region> threadLocal, StandardRegionDigraph origin) throws BundleException {
this.subgraphTraverser = new SubgraphTraverser();
+ this.bundleIdToRegionMapping = new StandardBundleIdToRegionMapping();
this.bundleContext = bundleContext;
this.threadLocal = threadLocal;
@@ -102,11 +99,11 @@
this.origin = origin;
if (origin != null) {
synchronized (origin.monitor) {
- this.originTimeStamp = origin.timeStamp.get();
+ this.originUpdateCount = origin.updateCount.get();
this.replace(origin, false);
}
} else {
- this.originTimeStamp = -1;
+ this.originUpdateCount = -1;
}
this.defaultRegion = null;
}
@@ -115,14 +112,14 @@
* {@inheritDoc}
*/
public Region createRegion(String regionName) throws BundleException {
- Region region = new BundleIdBasedRegion(regionName, this, this.bundleContext, this.threadLocal, this.monitor, this.timeStamp, this.bundleToRegion);
+ Region region = new BundleIdBasedRegion(regionName, this, this, this.bundleContext, this.threadLocal);
synchronized (this.monitor) {
if (getRegion(regionName) != null) {
throw new BundleException("Region '" + regionName + "' already exists", BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$ //$NON-NLS-2$
}
this.regions.add(region);
this.edges.put(region, EMPTY_EDGE_SET);
- this.timeStamp.incrementAndGet();
+ incrementUpdateCount();
}
notifyAdded(region);
return region;
@@ -166,7 +163,7 @@
headAdded = this.regions.add(headRegion);
connections.add(new StandardFilteredRegion(headRegion, filter));
this.edges.put(tailRegion, Collections.unmodifiableSet(connections));
- this.timeStamp.incrementAndGet();
+ incrementUpdateCount();
}
if (tailAdded) {
notifyAdded(tailRegion);
@@ -248,9 +245,7 @@
* {@inheritDoc}
*/
public Region getRegion(long bundleId) {
- synchronized (this.monitor) {
- return bundleToRegion.get(bundleId);
- }
+ return this.bundleIdToRegionMapping.getRegion(bundleId);
}
/**
@@ -277,7 +272,7 @@
}
}
}
- this.timeStamp.incrementAndGet();
+ incrementUpdateCount();
}
}
@@ -410,12 +405,12 @@
throw new IllegalArgumentException("The replacement digraph is not a copy of this digraph."); //$NON-NLS-1$
Map<Region, Set<FilteredRegion>> filteredRegions = replacement.getFilteredRegions();
synchronized (this.monitor) {
- if (check && this.timeStamp.get() != replacement.originTimeStamp) {
- throw new BundleException("The origin timestamp has changed since the replacement copy was created.", BundleException.INVALID_OPERATION); //$NON-NLS-1$
+ if (check && this.updateCount.get() != replacement.originUpdateCount) {
+ throw new BundleException("The origin update count has changed since the replacement copy was created.", BundleException.INVALID_OPERATION); //$NON-NLS-1$
}
this.regions.clear();
this.edges.clear();
- this.bundleToRegion.clear();
+ this.bundleIdToRegionMapping.clear();
for (Region original : filteredRegions.keySet()) {
Region copy = this.createRegion(original.getName());
for (Long id : original.getBundleIds()) {
@@ -429,13 +424,16 @@
this.connect(tailRegion, headFilter.getFilter(), headRegion);
}
}
- this.timeStamp.incrementAndGet();
+ incrementUpdateCount();
if (check) {
- replacement.originTimeStamp = this.timeStamp.get();
+ replacement.originUpdateCount = this.updateCount.get();
}
}
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public ResolverHookFactory getResolverHookFactory() {
return resolverHookFactory;
@@ -445,27 +443,42 @@
return bundleCollisionHook;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public EventHook getBundleEventHook() {
return bundleEventHook;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public FindHook getBundleFindHook() {
return bundleFindHook;
}
+ /**
+ * {@inheritDoc}
+ */
@SuppressWarnings("deprecation")
@Override
public org.osgi.framework.hooks.service.EventHook getServiceEventHook() {
return serviceEventHook;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public org.osgi.framework.hooks.service.FindHook getServiceFindHook() {
return serviceFindHook;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void setDefaultRegion(Region defaultRegion) {
if (this.regions.contains(defaultRegion) || defaultRegion == null) {
@@ -475,8 +488,47 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Region getDefaultRegion() {
return this.defaultRegion;
}
+
+ @Override
+ public void associateBundleWithRegion(long bundleId, Region region) throws BundleException {
+ this.bundleIdToRegionMapping.associateBundleWithRegion(bundleId, region);
+ incrementUpdateCount();
+ }
+
+ private void incrementUpdateCount() {
+ synchronized (this.monitor) {
+ this.updateCount.incrementAndGet();
+ }
+
+ }
+
+ @Override
+ public void dissociateBundle(long bundleId) {
+ this.bundleIdToRegionMapping.dissociateBundle(bundleId);
+ incrementUpdateCount();
+ }
+
+ @Override
+ public boolean isBundleAssociatedWithRegion(long bundleId, Region region) {
+ return this.bundleIdToRegionMapping.isBundleAssociatedWithRegion(bundleId, region);
+ }
+
+ @Override
+ public Set<Long> getBundleIds(Region region) {
+ return this.bundleIdToRegionMapping.getBundleIds(region);
+ }
+
+ @Override
+ public void clear() {
+ this.bundleIdToRegionMapping.clear();
+ incrementUpdateCount();
+ }
+
}