blob: 9a88abc3357512e262222b838e5475a7702efae9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2015 VMware Inc.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.equinox.internal.region.hook;
import java.util.Arrays;
import java.util.Collection;
import org.eclipse.equinox.region.*;
import org.osgi.framework.Bundle;
import org.osgi.framework.hooks.bundle.CollisionHook;
/**
* {@link RegionBundleCollisionHook} manages the collision policy of duplicate bundles.
*
* <strong>Concurrent Semantics</strong><br />
* Thread safe.
*/
public final class RegionBundleCollisionHook implements CollisionHook {
private final RegionDigraph regionDigraph;
private final ThreadLocal<Region> threadLocal;
public RegionBundleCollisionHook(RegionDigraph regionDigraph, ThreadLocal<Region> threadLocal) {
this.regionDigraph = regionDigraph;
this.threadLocal = threadLocal;
}
private Region getInstallRegion(Bundle originBundle) {
/*
* BundleIdBasedRegion sets thread local to install bundles into arbitrary regions. If this is not set, the
* bundle inherits the region of the origin bundle.
*/
Region installRegion = this.threadLocal.get();
if (installRegion != null) {
return installRegion;
}
return this.regionDigraph.getRegion(originBundle);
}
/**
* {@inheritDoc}
*/
@Override
public void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates) {
Region targetRegion = getInstallRegion(target);
if (targetRegion == null) {
// don't know if this is a collision or not just return all
return;
}
// First check the collision candidates from the perspective of the
// installing/updating targetRegion
VisitorFromTarget fromTarget = new VisitorFromTarget(collisionCandidates);
targetRegion.visitSubgraph(fromTarget);
Collection<Bundle> collisions = fromTarget.getAllowed();
if (collisions.isEmpty()) {
// must do a sanity check to make sure the newly installed/updated bundle
// does not collide from the perspective of the collision candidate regions
for (Bundle collisionCandidate : collisionCandidates) {
Region candidateRegion = regionDigraph.getRegion(collisionCandidate);
if (candidateRegion == null) {
// we assume the candidate has been uninstalled; do not consider the candidate as a collision
continue;
}
// we know the collision candidates all have the BSN/Version that collide.
// we use the collision candidate and pretend it is part of the target region
// to see if we can see it from the candidateRegion
VisitorFromCandidate fromCandidate = new VisitorFromCandidate(Arrays.asList(collisionCandidate), targetRegion);
candidateRegion.visitSubgraph(fromCandidate);
collisions = fromCandidate.getAllowed();
if (!collisions.isEmpty()) {
// we can break at the first one since it only takes one to fail the install/update
break;
}
}
}
collisionCandidates.retainAll(collisions);
}
class VisitorFromTarget extends RegionDigraphVisitorBase<Bundle> {
VisitorFromTarget(Collection<Bundle> candidates) {
super(candidates);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean contains(Region region, Bundle candidate) {
return region.contains(candidate);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isAllowed(Bundle candidate, RegionFilter filter) {
return filter.isAllowed(candidate);
}
}
class VisitorFromCandidate extends RegionDigraphVisitorBase<Bundle> {
private final Region targetRegion;
VisitorFromCandidate(Collection<Bundle> candidates, Region targetRegion) {
super(candidates);
this.targetRegion = targetRegion;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean contains(Region region, Bundle candidate) {
return region.equals(targetRegion);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isAllowed(Bundle candidate, RegionFilter filter) {
return filter.isAllowed(candidate);
}
}
}