| /******************************************************************************* |
| * Copyright (c) 2011, 2015 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.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); |
| } |
| |
| } |
| } |