/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.security.AccessController; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.aries.subsystem.core.archive.DynamicImportPackageHeader; import org.apache.aries.subsystem.core.archive.DynamicImportPackageRequirement; import org.apache.aries.subsystem.core.internal.BundleResourceInstaller.BundleConstituent; import org.apache.aries.subsystem.core.internal.StartAction.Restriction; import org.eclipse.equinox.region.Region; import org.eclipse.equinox.region.RegionDigraph.FilteredRegion; import org.eclipse.equinox.region.RegionDigraphVisitor; import org.eclipse.equinox.region.RegionFilter; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.hooks.weaving.WovenClass; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRevision; import org.osgi.framework.wiring.BundleWiring; import org.osgi.framework.wiring.FrameworkWiring; import org.osgi.service.subsystem.Subsystem; import org.osgi.service.subsystem.SubsystemException; public class WovenClassListener implements org.osgi.framework.hooks.weaving.WovenClassListener { private static class RegionUpdaterInfo { private final Region head; private final Collection<DynamicImportPackageRequirement> requirements; private final Region tail; public RegionUpdaterInfo(Region tail, Region head) { this.tail = tail; this.head = head; requirements = new ArrayList<DynamicImportPackageRequirement>(); } public Region head() { return head; } public void requirement(DynamicImportPackageRequirement requirement) { requirements.add(requirement); } public Collection<DynamicImportPackageRequirement> requirements() { return requirements; } public Region tail() { return tail; } } private final BundleContext context; private final Subsystems subsystems; public WovenClassListener(BundleContext context, Subsystems subsystems) { this.context = context; this.subsystems = subsystems; } @Override public void modified(WovenClass wovenClass) { if (wovenClass.getState() != WovenClass.TRANSFORMED) { // Dynamic package imports must be added when the woven class is in // the transformed state in order to ensure the class will load once // the defined state is reached. return; } List<String> dynamicImports = wovenClass.getDynamicImports(); if (dynamicImports.isEmpty()) { // Nothing to do if there are no dynamic imports. return; } BundleWiring wiring = wovenClass.getBundleWiring(); Bundle bundle = wiring.getBundle(); BundleRevision revision = bundle.adapt(BundleRevision.class); BundleConstituent constituent = new BundleConstituent(null, revision); Collection<BasicSubsystem> basicSubsystems = subsystems.getSubsystemsByConstituent(constituent); BasicSubsystem subsystem = basicSubsystems.iterator().next(); // Find the scoped subsystem in the region. subsystem = scopedSubsystem(subsystem); if (subsystem.getSubsystemId() == 0) { // The root subsystem needs no sharing policy. return; } if (EnumSet.of(Subsystem.State.INSTALLING, Subsystem.State.INSTALLED).contains(subsystem.getState())) { // The scoped subsystem must be resolved before adding dynamic // package imports to the sharing policy in order to minimize // unpredictable wirings. Resolving the scoped subsystem will also // resolve all of the unscoped subsystems in the region. AccessController.doPrivileged(new StartAction(subsystem, subsystem, subsystem, Restriction.RESOLVE_ONLY)); } Bundle systemBundle = context.getBundle(org.osgi.framework.Constants.SYSTEM_BUNDLE_LOCATION); FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class); // The following map tracks all of the necessary updates as each dynamic // import is processed. The key is the tail region of the connection // whose filter needs updating. Map<Region, RegionUpdaterInfo> updates = new HashMap<Region, RegionUpdaterInfo>(); for (String dynamicImport : dynamicImports) { // For each dynamic import, collect the necessary update information. DynamicImportPackageHeader header = new DynamicImportPackageHeader(dynamicImport); List<DynamicImportPackageRequirement> requirements = header.toRequirements(revision); for (DynamicImportPackageRequirement requirement : requirements) { Collection<BundleCapability> providers = frameworkWiring.findProviders(requirement); if (providers.isEmpty()) { // If nothing provides a capability matching the dynamic // import, no updates are made. continue; } addSharingPolicyUpdates(requirement, subsystem, providers, updates); } } // Now update each sharing policy only once. for (RegionUpdaterInfo update : updates.values()) { RegionUpdater updater = new RegionUpdater(update.tail(), update.head()); try { updater.addRequirements(update.requirements()); } catch (IllegalStateException e) { // Something outside of the subsystems implementation has // deleted the edge between the parent and child subsystems. // Assume the dynamic import sharing policy is being handled // elsewhere. See ARIES-1429. } catch (Exception e) { throw new SubsystemException(e); } } } private void addSharingPolicyUpdates( final DynamicImportPackageRequirement requirement, final BasicSubsystem scopedSubsystem, final Collection<BundleCapability> providers, Map<Region, RegionUpdaterInfo> updates) { final List<BasicSubsystem> subsystems = new ArrayList<BasicSubsystem>(); final Map<Region, BasicSubsystem> regionToSubsystem = new HashMap<Region, BasicSubsystem>(); regionToSubsystem(scopedSubsystem, regionToSubsystem); scopedSubsystem.getRegion().visitSubgraph(new RegionDigraphVisitor() { private final List<BasicSubsystem> visited = new ArrayList<BasicSubsystem>(); @Override public void postEdgeTraverse(RegionFilter filter) { // Nothing. } @Override public boolean preEdgeTraverse(RegionFilter filter) { return true; } @Override public boolean visit(Region region) { BasicSubsystem subsystem = regionToSubsystem.get(region); if (subsystem == null || subsystem.isRoot()) { // Don't mess with regions not created by the subsystem // implementation. Also, the root subsystem never has a // sharing policy. return false; } if (!visited.isEmpty() && !subsystem.equals(scopedParent(visited.get(visited.size() - 1)))) { // We're only interested in walking up the scoped parent tree. return false; } visited.add(subsystem); if (!requirement.getPackageName().contains("*")) { for (BundleCapability provider : providers) { BundleRevision br = provider.getResource(); if (region.contains(br.getBundle())) { // The region contains a bundle providing a matching // capability, and the dynamic import does not contain a // wildcard. The requirement is therefore completely // satisfied. return false; } } } boolean allowed = false; Set<FilteredRegion> filters = region.getEdges(); for (FilteredRegion filteredRegion : filters) { RegionFilter filter = filteredRegion.getFilter(); if (filter.isAllowed(providers.iterator().next())) { // The region already allows matching capabilities // through so there is no need to update the sharing // policy. allowed = true; break; } } if (!allowed) { // The subsystem region requires a sharing policy update. subsystems.add(subsystem); } // Visit the next region. return true; } }); // Collect the information for the necessary sharing policy updates. for (BasicSubsystem subsystem : subsystems) { Region tail = subsystem.getRegion(); Region head = scopedParent(subsystem).getRegion(); RegionUpdaterInfo info = updates.get(tail); if (info == null) { info = new RegionUpdaterInfo(tail, head); updates.put(tail, info); } info.requirement(requirement); } } private void regionToSubsystem(BasicSubsystem subsystem, Map<Region, BasicSubsystem> map) { map.put(subsystem.getRegion(), subsystem); subsystem = scopedParent(subsystem); if (subsystem == null) { return; } regionToSubsystem(subsystem, map); } private BasicSubsystem scopedParent(BasicSubsystem subsystem) { Collection<Subsystem> parents = subsystem.getParents(); if (parents.isEmpty()) { return null; } subsystem = (BasicSubsystem)parents.iterator().next(); return scopedSubsystem(subsystem); } private BasicSubsystem scopedSubsystem(BasicSubsystem subsystem) { while (!subsystem.isScoped()) { subsystem = (BasicSubsystem)subsystem.getParents().iterator().next(); } return subsystem; } }