package org.apache.aries.subsystem.scope.impl; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.aries.subsystem.scope.Scope; import org.apache.aries.subsystem.scope.SharePolicy; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceReference; import org.osgi.framework.hooks.bundle.EventHook; import org.osgi.framework.hooks.resolver.ResolverHook; import org.osgi.framework.hooks.resolver.ResolverHookFactory; import org.osgi.framework.hooks.service.EventListenerHook; import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRequirement; import org.osgi.framework.wiring.BundleRevision; public class ScopeManager implements EventHook, EventListenerHook, org.osgi.framework.hooks.bundle.FindHook, org.osgi.framework.hooks.service.FindHook, ResolverHook, ResolverHookFactory { static final Map<String, ScopeImpl> installingBundleToScope = Collections.synchronizedMap(new HashMap<String, ScopeImpl>()); private final BundleContext bundleContext; private final Scopes scopes; public ScopeManager(BundleContext bundleContext) throws InvalidSyntaxException, IOException { this.bundleContext = bundleContext; scopes = new Scopes(bundleContext); } public ResolverHook begin(java.util.Collection<BundleRevision> triggers) { return this; } public void end() { } public void event(BundleEvent event, Collection<BundleContext> contexts) { int type = event.getType(); Bundle source = event.getBundle(); if (type == BundleEvent.INSTALLED) { // For bundle installed events, the origin is the bundle whose context // was used to install the source bundle. In this case, we need to be // sure the origin bundle is assigned to a scope. This is necessary to // ensure the next step will succeed. This condition may occur, for // example, during Scope Admin initialization. Bundle origin = event.getOrigin(); synchronized (scopes) { if (!scopes.contains(origin)) { scopes.addBundle(origin); } // If Scope Admin is not the installer, add the installed bundle to the // origin bundle's scope. This will occur whenever bundles are not // installed via Scope Admin. if (origin.getBundleId() != bundleContext.getBundle().getBundleId()) { scopes.addBundle(source, scopes.getScope(origin)); } // Otherwise, assign the installed bundle to the scope designated by the scope update. else { ScopeImpl scope = installingBundleToScope.remove(source.getLocation()); scopes.addBundle(source, scope); } } } // Now filter the event listeners, if necessary. Only bundles in the same scope as the // bundle undergoing the state change may see the event. The one exception is the // system bundle, which receives all events and sends events to all listeners. if (source.getBundleId() == 0) return; ScopeImpl scope = scopes.getScope(source); Collection<Bundle> bundles = scope.getBundles(); for (Iterator<BundleContext> i = contexts.iterator(); i.hasNext();) { BundleContext bc = i.next(); Bundle b = bc.getBundle(); if (b.getBundleId() != 0 && !bundles.contains(b)) { i.remove(); } } if (type == BundleEvent.UNINSTALLED) { // For bundle uninstalled events, remove the bundle from Scope Admin. // Note this must be done after filtering the event listeners or the // bundle will get added back. scopes.removeBundle(source); } } public void event(ServiceEvent event, Map<BundleContext, Collection<ListenerInfo>> listeners) { Bundle registrar = event.getServiceReference().getBundle(); ScopeImpl scope = scopes.getScope(registrar); for (Iterator<BundleContext> i = listeners.keySet().iterator(); i.hasNext();) { Bundle listener = i.next().getBundle(); if (!scope.getBundles().contains(listener)) i.remove(); } } public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) { for (Iterator<BundleCapability> i = candidates.iterator(); i.hasNext();) { if (filterMatch(requirement, i.next())) i.remove(); } } public void filterResolvable(Collection<BundleRevision> candidates) { for (Iterator<BundleRevision> i = candidates.iterator(); i.hasNext();) { BundleRevision candidate = i.next(); ScopeImpl scope = scopes.getScope(candidate.getBundle()); if (scope.isUpdating()) i.remove(); } } public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) { ScopeImpl scope = scopes.getScope(singleton.getRevision().getBundle()); for (Iterator<BundleCapability> i = collisionCandidates.iterator(); i.hasNext();) { BundleCapability collisionCandidate = i.next(); if (!scope.getBundles().contains(collisionCandidate.getRevision().getBundle())) { i.remove(); } } } public void find(BundleContext context, Collection<Bundle> bundles) { // The system bundle may see all bundles. if (context.getBundle().getBundleId() == 0) return; Scope scope = scopes.getScope(context.getBundle()); for (Iterator<Bundle> i = bundles.iterator(); i.hasNext();) { Bundle bundle = i.next(); // All bundles may see the system bundle. if (bundle.getBundleId() == 0) continue; // Otherwise, a bundle may only see other bundles within its scope. if (!scope.getBundles().contains(bundle)) i.remove(); } } public void find(BundleContext context, String name, String filter, boolean allServices, Collection<ServiceReference<?>> references) { // System bundle can see all services. if (context.getBundle().getBundleId() == 0) return; for (Iterator<ServiceReference<?>> i = references.iterator(); i.hasNext();) { if (filterMatch(context, i.next())) i.remove(); } } public Scope getRootScope() { return scopes.getRootScope(); } public Scope getScope(Bundle bundle) { return scopes.getScope(bundle); } public void shutdown() { scopes.clear(); } private boolean filterMatch(BundleRequirement requirement, BundleCapability capability) { Scope scope = scopes.getScope(requirement.getRevision().getBundle()); if (scope.getBundles().contains(capability.getRevision().getBundle())) return false; if (scope.getId() < scopes.getScope(capability.getRevision().getBundle()).getId()) { if (matchesDescendants(scope.getChildren(), capability, null)) return false; } return !matchesAncestry(scope, capability); } private boolean filterMatch(BundleContext context, ServiceReference<?> reference) { Scope scope = scopes.getScope(context.getBundle()); if (scope.getBundles().contains(reference.getBundle())) return false; if (scope.getId() < scopes.getScope(reference.getBundle()).getId()) { if (matchesDescendants(scope.getChildren(), reference)) return false; } return !matchesAncestry(scope, reference); } private boolean matchesPolicyAndContainsBundle(Scope scope, BundleCapability capability, String sharePolicyType) { if (matchesPolicy(scope, capability, sharePolicyType)) { if (scope.getBundles().contains(capability.getRevision().getBundle())) { return true; } } return false; } private boolean matchesPolicy(Scope scope, BundleCapability capability, String sharePolicyType) { List<SharePolicy> policies = scope.getSharePolicies(sharePolicyType).get(capability.getNamespace()); if (policies == null) return false; for (SharePolicy policy : policies) { if (policy.getFilter().matches(capability.getAttributes())) { return true; } } return false; } private boolean matchesAncestry(Scope scope, BundleCapability capability) { if (matchesPolicy(scope, capability, SharePolicy.TYPE_IMPORT)) { Scope parent = scope.getParent(); if (parent != null) { if (parent.getBundles().contains(capability.getRevision().getBundle())) return true; if (matchesDescendants(parent.getChildren(), capability, scope)) return true; return matchesAncestry(parent, capability); } } return false; } private boolean matchesAncestry(Scope scope, ServiceReference<?> reference) { if (matchesPolicy(scope, reference, SharePolicy.TYPE_IMPORT)) { Scope parent = scope.getParent(); if (parent != null) { if (parent.getBundles().contains(reference.getBundle())) return true; return matchesAncestry(parent, reference); } } return false; } private boolean matchesDescendant(Scope child, BundleCapability capability) { if (matchesPolicyAndContainsBundle(child, capability, SharePolicy.TYPE_EXPORT)) return true; return matchesDescendants(child.getChildren(), capability, null); } private boolean matchesDescendant(Scope child, ServiceReference<?> reference) { if (matchesPolicyAndContainsBundle(child, reference, SharePolicy.TYPE_EXPORT)) return true; return matchesDescendants(child.getChildren(), reference); } private boolean matchesDescendants(Collection<Scope> children, BundleCapability capability, Scope skip) { for (Scope child : children) { if (child.equals(skip)) continue; if (matchesDescendant(child, capability)) { return true; } } return false; } private boolean matchesDescendants(Collection<Scope> children, ServiceReference<?> reference) { for (Scope child : children) { if (matchesDescendant(child, reference)) { return true; } } return false; } private boolean matchesPolicyAndContainsBundle(Scope scope, ServiceReference<?> reference, String sharePolicyType) { if (matchesPolicy(scope, reference, sharePolicyType)) { if (scope.getBundles().contains(reference.getBundle())) { return true; } } return false; } private boolean matchesPolicy(Scope scope, ServiceReference<?> reference, String sharePolicyType) { List<SharePolicy> policies = scope.getSharePolicies(sharePolicyType).get("scope.share.service"); if (policies == null) return false; for (SharePolicy policy : policies) { if (policy.getFilter().match(reference)) { return true; } } return false; } }