public void downloadBundles()

in features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java [463:635]


    public void downloadBundles(DownloadManager manager,
                                String featureResolutionRange,
                                final FeaturesService.ServiceRequirementsBehavior serviceRequirements,
                                RepositoryManager repos,
                                SubsystemResolverCallback callback) throws Exception {
        for (Subsystem child : children) {
            child.downloadBundles(manager, featureResolutionRange, serviceRequirements, repos, callback);
        }

        // collect BundleInfos for given feature - both direct <feature>/<bundle>s and <feature>/<conditional>/<bundle>s
        final Map<BundleInfo, Conditional> infos = new HashMap<>();
        final Downloader downloader = manager.createDownloader();
        if (feature != null) {
            for (Conditional cond : feature.getConditional()) {
                if (!cond.isBlacklisted()) {
                    for (final BundleInfo bi : cond.getBundles()) {
                        // bundles from conditional features will be added as non-mandatory requirements
                        infos.put(bi, cond);
                    }
                }
            }
            for (BundleInfo bi : feature.getBundles()) {
                infos.put(bi, null);
            }
        }

        // features model doesn't have blacklisted entries removed, but marked as blacklisted - we now don't have
        // to download them
        //infos.keySet().removeIf(Blacklisting::isBlacklisted);
        for (Iterator<BundleInfo> iterator = infos.keySet().iterator(); iterator.hasNext(); ) {
            BundleInfo bi = iterator.next();
            if (bi.isBlacklisted()) {
                iterator.remove();
                if (callback != null) {
                    callback.bundleBlacklisted(bi);
                }
            }
        }

        // all downloaded bundles
        final Map<String, ResourceImpl> bundles = new ConcurrentHashMap<>();
        // resources for locations that were overriden in OSGi mode - to check whether the override should actually
        // take place, by checking resource's headers
        final Map<String, ResourceImpl> overrides = new ConcurrentHashMap<>();

        boolean removeServiceRequirements = serviceRequirementsBehavior(feature, serviceRequirements);

        // download collected BundleInfo locations
        for (Map.Entry<BundleInfo, Conditional> entry : infos.entrySet()) {
            final BundleInfo bi = entry.getKey();
            final String loc = bi.getLocation();
            downloader.download(loc, provider -> {
                // always download location (could be overriden)
                ResourceImpl resource = createResource(loc, getMetadata(provider), removeServiceRequirements);
                bundles.put(loc, resource);

                if (bi.isOverriden() == BundleInfo.BundleOverrideMode.OSGI) {
                    // also download original from original bundle URI to check if we should override by comparing
                    // symbolic name - requires MANIFEST.MF header access. If there should be no override, we'll get
                    // back to original URI
                    downloader.download(bi.getOriginalLocation(), provider2 -> {
                        ResourceImpl originalResource = createResource(bi.getOriginalLocation(),
                                getMetadata(provider2), removeServiceRequirements);
                        bundles.put(bi.getOriginalLocation(), originalResource);
                        // an entry in overrides map means that given location was overriden
                        overrides.put(loc, originalResource);
                    });
                }
            });
        }
        // download direct bundle: requirements - without consulting overrides
        for (Clause bundle : Parser.parseClauses(this.bundles.toArray(new String[this.bundles.size()]))) {
            final String loc = bundle.getName();
            downloader.download(loc, provider -> bundles.put(loc, createResource(loc, getMetadata(provider), removeServiceRequirements)));
        }
        // we *don't* have to download overrides separately - they're already taken into account from processed model

        // download additional libraries - only exported, so they're capabilities are taken into account during
        // resolution process
        if (feature != null) {
            for (Library library : feature.getLibraries()) {
                if (library.isExport()) {
                    final String loc = library.getLocation();
                    downloader.download(loc, provider -> bundles.put(loc, createResource(loc, getMetadata(provider), removeServiceRequirements)));
                }
            }
        }
        downloader.await();

        // opposite to what we had before. Currently bundles are already overriden at model level, but
        // as we finally have access to headers, we can compare symbolic names and if override mode is OSGi, then
        // we can restore original resource if there should be no override.
        Overrides.override(bundles, overrides);

        if (feature != null) {
            // Add conditionals
            Map<Conditional, Resource> resConds = new HashMap<>();
            for (Conditional cond : feature.getConditional()) {
                if (cond.isBlacklisted()) {
                    continue;
                }
                FeatureResource resCond = FeatureResource.build(feature, cond, featureResolutionRange, bundles);
                // feature's subsystem will optionally require conditional feature resource
                addIdentityRequirement(this, resCond, false);
                // but it's a mandatory requirement in other way
                addIdentityRequirement(resCond, this, true);
                installable.add(resCond);
                resConds.put(cond, resCond);
            }
            // Add features and make it require given subsystem that represents logical feature requirement
            FeatureResource resFeature = FeatureResource.build(feature, featureResolutionRange, bundles);
            addIdentityRequirement(resFeature, this);
            installable.add(resFeature);
            // Add dependencies
            for (Map.Entry<BundleInfo, Conditional> entry : infos.entrySet()) {
                final BundleInfo bi = entry.getKey();
                final String loc = bi.getLocation();
                final Conditional cond = entry.getValue();
                ResourceImpl res = bundles.get(loc);
                int sl = bi.getStartLevel() <= 0 ? feature.getStartLevel() : bi.getStartLevel();
                if (cond != null) {
                    // bundle of conditional feature will have mandatory requirement on it
                    addIdentityRequirement(res, resConds.get(cond), true);
                }
                boolean mandatory = !bi.isDependency() && cond == null;
                if (bi.isDependency()) {
                    addDependency(res, mandatory, bi.isStart(), sl, bi.isBlacklisted());
                } else {
                    doAddDependency(res, mandatory, bi.isStart(), sl, bi.isBlacklisted());
                }
            }
            for (Library library : feature.getLibraries()) {
                if (library.isExport()) {
                    final String loc = library.getLocation();
                    ResourceImpl res = bundles.get(loc);
                    addDependency(res, false, false, 0, false);
                }
            }
            for (String uri : feature.getResourceRepositories()) {
                BaseRepository repo = repos.getRepository(feature.getRepositoryUrl(), uri);
                for (Resource resource : repo.getResources()) {
                    ResourceImpl res = cloneResource(resource);
                    addDependency(res, false, true, 0, false);
                }
            }
        }
        for (Clause bundle : Parser.parseClauses(this.bundles.toArray(new String[this.bundles.size()]))) {
            final String loc = bundle.getName();
            boolean dependency = Boolean.parseBoolean(bundle.getAttribute("dependency"));
            boolean start = bundle.getAttribute("start") == null || Boolean.parseBoolean(bundle.getAttribute("start"));
            boolean blacklisted = bundle.getAttribute("blacklisted") != null && Boolean.parseBoolean(bundle.getAttribute("blacklisted"));
            int startLevel = 0;
            try {
                startLevel = Integer.parseInt(bundle.getAttribute("start-level"));
            } catch (NumberFormatException e) {
                // Ignore
            }
            if (dependency) {
                addDependency(bundles.get(loc), false, start, startLevel, blacklisted);
            } else {
                doAddDependency(bundles.get(loc), true, start, startLevel, blacklisted);
                // non dependency bundle will be added as osgi.identity req on type=osgi.bundle
                addIdentityRequirement(this, bundles.get(loc));
            }
        }
        // Compute dependencies
        for (DependencyInfo info : dependencies.values()) {
            installable.add(info.resource);
            // bundle resource will have a requirement on its feature's subsystem too
            // when bundle is declared with dependency="true", it will have a requirement on its region's subsystem
            addIdentityRequirement(info.resource, this, info.mandatory);
        }
    }