/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.aries.subsystem.core.internal; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.eclipse.equinox.region.Region; import org.eclipse.equinox.region.RegionDigraph; import org.eclipse.equinox.region.RegionDigraph.FilteredRegion; import org.eclipse.equinox.region.RegionFilter; import org.eclipse.equinox.region.RegionFilterBuilder; import org.osgi.framework.BundleException; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.namespace.service.ServiceNamespace; import org.osgi.resource.Requirement; public class RegionUpdater { public static final int MAX_ATTEMPTS_DEFAULT = 10; private final RegionDigraph digraph; private final Region head; private final Region tail; public RegionUpdater(Region tail, Region head) { if (head == null) throw new NullPointerException(); this.tail = tail; this.head = head; digraph = tail.getRegionDigraph(); } public void addRequirements(Collection<? extends Requirement> requirements) throws BundleException, InvalidSyntaxException { for (int i = 0; i < MAX_ATTEMPTS_DEFAULT; i++) { RegionDigraph copy = copyDigraph(); Region tail = copyTail(copy); Region head = copyHead(copy); Set<Long> bundleIds = copyBundleIds(tail); Map<String, RegionFilterBuilder> heads = copyHeadRegions(tail, copy); Map<String, RegionFilterBuilder> tails = copyTailRegions(tail, copy); copy.removeRegion(tail); tail = copy.createRegion(tail.getName()); addBundleIds(bundleIds, tail); RegionFilterBuilder builder = heads.get(head.getName()); if (builder == null) { // Something outside of the subsystems implementation has // deleted the edge between the parent and child subsystems. // See ARIES-1429. throw new IllegalStateException( new StringBuilder(tail.getName()) .append(" not connected to ") .append(head.getName()) .toString()); } if (requirements == null) { heads.put(head.getName(), null); } else { addRequirements(requirements, builder); } addHeadRegions(heads, tail, copy); addTailRegions(tails, tail, copy); // Replace the current digraph. try { digraph.replace(copy); } catch (BundleException e) { // Something modified digraph since the copy was made. if (i < MAX_ATTEMPTS_DEFAULT) // There are more attempts to make. continue; // Number of attempts has been exhausted. throw e; } // Success! No need to continue looping. break; } } private void addBundleIds(Set<Long> ids, Region region) throws BundleException { for (Long id : ids) region.addBundle(id); } private void addHeadRegions(Map<String, RegionFilterBuilder> heads, Region tail, RegionDigraph digraph) throws BundleException { for (Map.Entry<String, RegionFilterBuilder> entry : heads.entrySet()) { RegionFilterBuilder builder = entry.getValue(); if (builder == null) { continue; } tail.connectRegion(digraph.getRegion(entry.getKey()), builder.build()); } } private void addTailRegions(Map<String, RegionFilterBuilder> tails, Region head, RegionDigraph digraph) throws BundleException { for (Map.Entry<String, RegionFilterBuilder> entry : tails.entrySet()) digraph.getRegion(entry.getKey()).connectRegion(head, entry.getValue().build()); } private void addRequirements(Collection<? extends Requirement> requirements, RegionFilterBuilder builder) throws InvalidSyntaxException { for (Requirement requirement : requirements) { String namespace = requirement.getNamespace(); // The osgi.service namespace requires translation. if (ServiceNamespace.SERVICE_NAMESPACE.equals(namespace)) namespace = RegionFilter.VISIBLE_SERVICE_NAMESPACE; String filter = requirement.getDirectives().get(IdentityNamespace.REQUIREMENT_FILTER_DIRECTIVE); // A null filter means import everything from that namespace. if (filter == null) builder.allowAll(namespace); else builder.allow(namespace, filter); } } private Set<Long> copyBundleIds(Region region) { return region.getBundleIds(); } private RegionDigraph copyDigraph() throws BundleException { return digraph.copy(); } private Region copyHead(RegionDigraph digraph) { return digraph.getRegion(head.getName()); } private Map<String, RegionFilterBuilder> copyHeadRegions(Region tail, RegionDigraph digraph) throws InvalidSyntaxException { Map<String, RegionFilterBuilder> result = new HashMap<String, RegionFilterBuilder>(); for (FilteredRegion edge : tail.getEdges()) result.put(edge.getRegion().getName(), createRegionFilterBuilder(edge.getFilter().getSharingPolicy(), digraph)); return result; } private Region copyTail(RegionDigraph digraph) { return digraph.getRegion(tail.getName()); } private Map<String, RegionFilterBuilder> copyTailRegions(Region tail, RegionDigraph digraph) throws InvalidSyntaxException { Map<String, RegionFilterBuilder> result = new HashMap<String, RegionFilterBuilder>(); for (Region head : digraph.getRegions()) { if (head.equals(tail)) continue; for (FilteredRegion edge : head.getEdges()) if (edge.getRegion().equals(tail)) result.put(head.getName(), createRegionFilterBuilder(edge.getFilter().getSharingPolicy(), digraph)); } return result; } private RegionFilterBuilder createRegionFilterBuilder(Map<String, Collection<String>> sharingPolicy, RegionDigraph digraph) throws InvalidSyntaxException { RegionFilterBuilder result = digraph.createRegionFilterBuilder(); for (Map.Entry<String, Collection<String>> entry : sharingPolicy.entrySet()) for (String filter : entry.getValue()) result.allow(entry.getKey(), filter); return result; } }