/*
* 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;
}
}