
org.eclipse.equinox.internal.region.StandardRegionDigraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.equinox.region Show documentation
Show all versions of org.eclipse.equinox.region Show documentation
Provides the concept of a region used to isolate bundles according to the configured sharing policy, which is applied across the framework using the OSGi hook APIs.
The newest version!
/*******************************************************************************
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SpringSource, a division of VMware - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.equinox.internal.region;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.equinox.internal.region.hook.*;
import org.eclipse.equinox.region.*;
import org.osgi.framework.*;
import org.osgi.framework.hooks.bundle.EventHook;
import org.osgi.framework.hooks.bundle.FindHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
/**
* {@link StandardRegionDigraph} is the default implementation of {@link RegionDigraph}.
*
*
* Concurrent Semantics
*
* Thread safe.
*
*/
public final class StandardRegionDigraph implements BundleIdToRegionMapping, RegionDigraph {
private static final Set EMPTY_EDGE_SET = Collections.unmodifiableSet(new HashSet());
// This monitor guards the modifications and read operations on the digraph as well as
// bundle id modifications of all regions in this digraph
private final Object monitor = new Object();
private final Set regions = new HashSet();
// Alien calls may be made to the following object while this.monitor is locked
// as this.monitor is higher in the lock hierarchy than this object's own monitor.
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. */
private final Map> edges = new HashMap>();
private final BundleContext bundleContext;
private final ThreadLocal threadLocal;
private final SubgraphTraverser subgraphTraverser;
private final Object bundleCollisionHook;
private final org.osgi.framework.hooks.bundle.EventHook bundleEventHook;
private final org.osgi.framework.hooks.bundle.FindHook bundleFindHook;
@SuppressWarnings("deprecation")
private final org.osgi.framework.hooks.service.EventHook serviceEventHook;
private final org.osgi.framework.hooks.service.FindHook serviceFindHook;
private final ResolverHookFactory resolverHookFactory;
private final StandardRegionDigraph origin;
// Guarded by the origin monitor
private long originUpdateCount;
private final AtomicLong updateCount = new AtomicLong();
private volatile Region defaultRegion;
public StandardRegionDigraph(StandardRegionDigraph origin) throws BundleException {
this(null, null, origin);
}
public StandardRegionDigraph(BundleContext bundleContext, ThreadLocal threadLocal) throws BundleException {
this(bundleContext, threadLocal, null);
}
private StandardRegionDigraph(BundleContext bundleContext, ThreadLocal threadLocal, StandardRegionDigraph origin) throws BundleException {
this.subgraphTraverser = new SubgraphTraverser();
this.bundleIdToRegionMapping = new StandardBundleIdToRegionMapping();
this.bundleContext = bundleContext;
this.threadLocal = threadLocal;
// Note we are safely escaping this only because we know the hook impls
// do not escape the digraph to other threads on construction.
this.resolverHookFactory = new RegionResolverHookFactory(this);
this.bundleFindHook = new RegionBundleFindHook(this, bundleContext == null ? 0 : bundleContext.getBundle().getBundleId());
this.bundleEventHook = new RegionBundleEventHook(this, this.threadLocal, bundleContext == null ? 0 : bundleContext.getBundle().getBundleId());
Object hook;
try {
hook = new RegionBundleCollisionHook(this, this.threadLocal);
} catch (Throwable t) {
hook = null;
}
this.bundleCollisionHook = hook;
this.serviceFindHook = new RegionServiceFindHook(this);
this.serviceEventHook = new RegionServiceEventHook(this);
this.origin = origin;
if (origin != null) {
synchronized (origin.monitor) {
this.originUpdateCount = origin.updateCount.get();
this.replace(origin, false);
}
} else {
this.originUpdateCount = -1;
}
this.defaultRegion = null;
}
/**
* {@inheritDoc}
*/
public Region createRegion(String regionName) throws BundleException {
return createRegion(regionName, true);
}
private Region createRegion(String regionName, boolean notify) throws BundleException {
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);
incrementUpdateCount();
}
if (notify) {
notifyAdded(region);
}
return region;
}
/**
* {@inheritDoc}
*/
public void connect(Region tailRegion, RegionFilter filter, Region headRegion) throws BundleException {
if (tailRegion == null)
throw new IllegalArgumentException("The tailRegion must not be null."); //$NON-NLS-1$
if (filter == null)
throw new IllegalArgumentException("The filter must not be null."); //$NON-NLS-1$
if (headRegion == null)
throw new IllegalArgumentException("The headRegion must not be null."); //$NON-NLS-1$
if (headRegion.equals(tailRegion)) {
throw new BundleException("Cannot connect region '" + headRegion + "' to itself", BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$ //$NON-NLS-2$
}
if (tailRegion.getRegionDigraph() != this)
throw new IllegalArgumentException("The tailRegion does not belong to this digraph."); //$NON-NLS-1$
if (headRegion.getRegionDigraph() != this)
throw new IllegalArgumentException("The headRegion does not belong to this digraph."); //$NON-NLS-1$
boolean tailAdded = false;
boolean headAdded = false;
synchronized (this.monitor) {
Set connections = this.edges.get(tailRegion);
if (connections == null) {
connections = new HashSet();
} else {
connections = new HashSet(connections);
for (FilteredRegion edge : connections) {
if (headRegion.equals(edge.getRegion())) {
throw new BundleException("Region '" + tailRegion + "' is already connected to region '" + headRegion, BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
checkFilterDoesNotAllowExistingBundle(tailRegion, filter);
tailAdded = this.regions.add(tailRegion);
headAdded = this.regions.add(headRegion);
connections.add(new StandardFilteredRegion(headRegion, filter));
this.edges.put(tailRegion, Collections.unmodifiableSet(connections));
incrementUpdateCount();
}
if (tailAdded) {
notifyAdded(tailRegion);
}
if (headAdded) {
notifyAdded(headRegion);
}
}
private void checkFilterDoesNotAllowExistingBundle(Region tailRegion, RegionFilter filter) {
// TODO: enumerate the bundles in the region and check the filter does not allow any of them
}
/**
* {@inheritDoc}
*/
public Iterator iterator() {
synchronized (this.monitor) {
Set snapshot = new HashSet(this.regions.size());
snapshot.addAll(this.regions);
return snapshot.iterator();
}
}
/**
* {@inheritDoc}
*/
public Set getEdges(Region tailRegion) {
synchronized (this.monitor) {
// Cope with the case where tailRegion is not in the digraph
Set edgeSet = this.edges.get(tailRegion);
return edgeSet == null ? EMPTY_EDGE_SET : edgeSet;
}
}
static class StandardFilteredRegion implements FilteredRegion {
private Region region;
private RegionFilter regionFilter;
StandardFilteredRegion(Region region, RegionFilter regionFilter) {
this.region = region;
this.regionFilter = regionFilter;
}
public Region getRegion() {
return this.region;
}
public RegionFilter getFilter() {
return this.regionFilter;
}
}
/**
* {@inheritDoc}
*/
public Region getRegion(String regionName) {
synchronized (this.monitor) {
for (Region region : this) {
if (regionName.equals(region.getName())) {
return region;
}
}
return null;
}
}
/**
* {@inheritDoc}
*/
public Region getRegion(Bundle bundle) {
return getRegion(bundle.getBundleId());
}
/**
* {@inheritDoc}
*/
public Region getRegion(long bundleId) {
synchronized (this.monitor) {
return this.bundleIdToRegionMapping.getRegion(bundleId);
}
}
/**
* {@inheritDoc}
*/
public void removeRegion(Region region) {
if (region == null)
throw new IllegalArgumentException("The region cannot be null."); //$NON-NLS-1$
notifyRemoving(region);
synchronized (this.monitor) {
if (this.defaultRegion != null && this.defaultRegion.equals(region)) {
this.defaultRegion = null;
}
this.regions.remove(region);
this.edges.remove(region);
for (Region r : this.edges.keySet()) {
Set edgeSet = this.edges.get(r);
for (FilteredRegion edge : edgeSet) {
if (region.equals(edge.getRegion())) {
Set mutableEdgeSet = new HashSet(edgeSet);
mutableEdgeSet.remove(edge);
this.edges.put(r, Collections.unmodifiableSet(mutableEdgeSet));
break;
}
}
}
this.bundleIdToRegionMapping.dissociateRegion(region);
incrementUpdateCount();
}
}
/**
* {@inheritDoc}
*/
public String toString() {
synchronized (this.monitor) {
StringBuffer s = new StringBuffer();
boolean first = true;
s.append("RegionDigraph{"); //$NON-NLS-1$
for (Region r : this) {
if (!first) {
s.append(", "); //$NON-NLS-1$
}
s.append(r);
first = false;
}
s.append("}"); //$NON-NLS-1$
s.append("["); //$NON-NLS-1$
first = true;
for (Region r : this) {
Set edgeSet = this.edges.get(r);
if (edgeSet != null) {
for (FilteredRegion filteredRegion : edgeSet) {
if (!first) {
s.append(", "); //$NON-NLS-1$
}
s.append(r + "->" + filteredRegion.getRegion()); //$NON-NLS-1$
first = false;
}
}
}
s.append("]"); //$NON-NLS-1$
return s.toString();
}
}
public Set getRegions() {
Set result = new HashSet();
synchronized (this.monitor) {
result.addAll(this.regions);
}
return result;
}
public RegionFilterBuilder createRegionFilterBuilder() {
return new StandardRegionFilterBuilder();
}
private void notifyAdded(Region region) {
Set listeners = getListeners();
for (RegionLifecycleListener listener : listeners) {
listener.regionAdded(region);
}
}
private void notifyRemoving(Region region) {
Set listeners = getListeners();
for (RegionLifecycleListener listener : listeners) {
listener.regionRemoving(region);
}
}
private Set getListeners() {
if (this.bundleContext == null)
return Collections.emptySet();
Set listeners = new HashSet();
try {
Collection> listenerServiceReferences = this.bundleContext.getServiceReferences(RegionLifecycleListener.class, null);
for (ServiceReference listenerServiceReference : listenerServiceReferences) {
RegionLifecycleListener regionLifecycleListener = this.bundleContext.getService(listenerServiceReference);
if (regionLifecycleListener != null) {
listeners.add(regionLifecycleListener);
}
}
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
return listeners;
}
/**
* {@inheritDoc}
*/
public void visitSubgraph(Region startingRegion, RegionDigraphVisitor visitor) {
this.subgraphTraverser.visitSubgraph(startingRegion, visitor);
}
/**
* Returns a snapshot of filtered regions
*
* @return a snapshot of filtered regions
*/
Map> getFilteredRegions() {
synchronized (this.monitor) {
return new HashMap>(this.edges);
}
}
/**
* {@inheritDoc}
*/
public RegionDigraphPersistence getRegionDigraphPersistence() {
return new StandardRegionDigraphPersistence();
}
/**
* {@inheritDoc}
*/
@Override
public RegionDigraph copy() throws BundleException {
return new StandardRegionDigraph(this);
}
/**
* {@inheritDoc}
*/
@Override
public void replace(RegionDigraph digraph) throws BundleException {
replace(digraph, true);
}
private void replace(RegionDigraph digraph, boolean check) throws BundleException {
if (!(digraph instanceof StandardRegionDigraph))
throw new IllegalArgumentException("Only digraphs of type '" + StandardRegionDigraph.class.getName() + "' are allowed: " + digraph.getClass().getName()); //$NON-NLS-1$ //$NON-NLS-2$
StandardRegionDigraph replacement = (StandardRegionDigraph) digraph;
if (check && replacement.origin != this)
throw new IllegalArgumentException("The replacement digraph is not a copy of this digraph."); //$NON-NLS-1$
// notify removing first, and outside the monitor lock
final Set removed = getRegions();
removed.removeAll(replacement.getRegions());
for (Region region : removed) {
notifyRemoving(region);
}
Map> filteredRegions = replacement.getFilteredRegions();
final Set added = new HashSet();
synchronized (this.monitor) {
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$
}
Map nameToRegion = new HashMap();
for (Region region : regions) {
nameToRegion.put(region.getName(), region);
}
this.regions.clear();
this.edges.clear();
this.bundleIdToRegionMapping.clear();
for (Region original : filteredRegions.keySet()) {
Region copy = nameToRegion.get(original.getName());
if (copy != null) {
// reuse the previous region object
regions.add(copy);
edges.put(copy, EMPTY_EDGE_SET);
} else {
// create a new one
copy = this.createRegion(original.getName(), false);
// collect added for notifying later ouside the lock
added.add(copy);
}
for (Long id : original.getBundleIds()) {
copy.addBundle(id);
}
}
for (Map.Entry> connection : filteredRegions.entrySet()) {
Region tailRegion = this.getRegion(connection.getKey().getName());
for (FilteredRegion headFilter : connection.getValue()) {
Region headRegion = this.getRegion(headFilter.getRegion().getName());
this.connect(tailRegion, headFilter.getFilter(), headRegion);
}
}
incrementUpdateCount();
if (check) {
replacement.originUpdateCount = this.updateCount.get();
}
}
// Now notify of additions outside the lock
for (Region region : added) {
notifyAdded(region);
}
}
/**
* {@inheritDoc}
*/
@Override
public ResolverHookFactory getResolverHookFactory() {
return resolverHookFactory;
}
Object getBundleCollisionHook() {
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) {
synchronized (this.monitor) {
if (defaultRegion != null) {
checkRegionExists(defaultRegion);
}
this.defaultRegion = defaultRegion;
}
}
/**
* {@inheritDoc}
*/
@Override
public Region getDefaultRegion() {
return this.defaultRegion;
}
/**
* {@inheritDoc}
*/
@Override
public void associateBundleWithRegion(long bundleId, Region region) throws BundleException {
synchronized (this.monitor) {
checkRegionExists(region);
this.bundleIdToRegionMapping.associateBundleWithRegion(bundleId, region);
incrementUpdateCount();
}
}
private void checkRegionExists(Region region) {
if (!this.regions.contains(region)) {
throw new IllegalStateException("Operation not allowed on region " + region.getName() + " which is not part of a digraph"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void incrementUpdateCount() {
synchronized (this.monitor) {
this.updateCount.incrementAndGet();
}
}
/**
* {@inheritDoc}
*/
@Override
public void dissociateBundleFromRegion(long bundleId, Region region) {
synchronized (this.monitor) {
checkRegionExists(region);
this.bundleIdToRegionMapping.dissociateBundleFromRegion(bundleId, region);
incrementUpdateCount();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isBundleAssociatedWithRegion(long bundleId, Region region) {
synchronized (this.monitor) {
return this.bundleIdToRegionMapping.isBundleAssociatedWithRegion(bundleId, region);
}
}
/**
* {@inheritDoc}
*/
@Override
public Set getBundleIds(Region region) {
synchronized (this.monitor) {
return this.bundleIdToRegionMapping.getBundleIds(region);
}
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
synchronized (this.monitor) {
this.bundleIdToRegionMapping.clear();
incrementUpdateCount();
}
}
/**
* {@inheritDoc}
*/
@Override
public void dissociateRegion(Region region) {
synchronized (this.monitor) {
this.bundleIdToRegionMapping.dissociateRegion(region);
incrementUpdateCount();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy