Search in sources :

Example 1 with FeatureUpdate

use of io.fabric8.patch.management.FeatureUpdate in project fabric8 by jboss-fuse.

the class ServiceImpl method bundleUpdatesInPatch.

/**
 * Returns a list of {@link BundleUpdate} for single patch, taking into account already discovered updates
 * @param patch
 * @param allBundles
 * @param bundleUpdateLocations out parameter that gathers update locations for bundles across patches
 * @param history
 * @param updatesForBundleKeys
 * @param kind
 * @param coreBundles
 * @param featureUpdatesInThisPatch
 * @return
 * @throws IOException
 */
private List<BundleUpdate> bundleUpdatesInPatch(Patch patch, Bundle[] allBundles, Map<Bundle, String> bundleUpdateLocations, BundleVersionHistory history, Map<String, BundleUpdate> updatesForBundleKeys, PatchKind kind, Map<String, Bundle> coreBundles, List<FeatureUpdate> featureUpdatesInThisPatch) throws Exception {
    List<BundleUpdate> updatesInThisPatch = new LinkedList<>();
    // for ROLLUP patch we can check which bundles AREN'T updated by this patch - we have to reinstall them
    // at the same version as existing one. "no update" means "require install after clearing cache"
    // Initially all bundles need update. If we find an update in patch, we remove a key from this map
    Map<String, Bundle> updateNotRequired = new LinkedHashMap<>();
    // // let's keep {symbolic name -> list of versions} mapping
    // MultiMap<String, Version> allBundleVersions = new MultiMap<>();
    // bundle location -> bundle key (symbolic name|updateable version)
    Map<String, String> locationsOfBundleKeys = new HashMap<>();
    for (Bundle b : allBundles) {
        if (b.getSymbolicName() == null) {
            continue;
        }
        Version v = b.getVersion();
        Version updateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
        String key = String.format("%s|%s", stripSymbolicName(b.getSymbolicName()), updateableVersion.toString());
        // symbolic name, differing at micro version only
        if (!coreBundles.containsKey(stripSymbolicName(b.getSymbolicName()))) {
            updateNotRequired.put(key, b);
        } else {
            // let's key core (etc/startup.properties) bundles by symbolic name only - there should be only
            // one version of symbolic name
            updateNotRequired.put(stripSymbolicName(b.getSymbolicName()), b);
        }
        // allBundleVersions.put(stripSymbolicName(b.getSymbolicName()), b.getVersion());
        String location = b.getLocation();
        if (location != null && location.startsWith("mvn:") && location.contains("//")) {
            // special case for mvn:org.ops4j.pax.url/pax-url-wrap/2.4.7//uber
            location = location.replace("//", "/jar/");
        }
        locationsOfBundleKeys.put(location, key);
    }
    // let's prepare a set of bundle keys that are part of features that will be updated/reinstalled - those
    // bundle keys don't have to be reinstalled separately
    Set<String> bundleKeysFromFeatures = new HashSet<>();
    if (featureUpdatesInThisPatch != null) {
        for (FeatureUpdate featureUpdate : featureUpdatesInThisPatch) {
            if (featureUpdate.getName() != null) {
                // this is either installation or update of single feature
                String fName = featureUpdate.getName();
                String fVersion = featureUpdate.getPreviousVersion();
                Feature f = featuresService.getFeature(fName, fVersion);
                for (BundleInfo bundleInfo : f.getBundles()) {
                    if (/*!bundleInfo.isDependency() && */
                    locationsOfBundleKeys.containsKey(bundleInfo.getLocation())) {
                        bundleKeysFromFeatures.add(locationsOfBundleKeys.get(bundleInfo.getLocation()));
                    }
                }
                for (Conditional cond : f.getConditional()) {
                    for (BundleInfo bundleInfo : cond.getBundles()) {
                        if (/*!bundleInfo.isDependency() && */
                        locationsOfBundleKeys.containsKey(bundleInfo.getLocation())) {
                            bundleKeysFromFeatures.add(locationsOfBundleKeys.get(bundleInfo.getLocation()));
                        }
                    }
                }
            }
        }
    }
    for (String newLocation : patch.getPatchData().getBundles()) {
        // [symbolicName, version] of the new bundle
        String[] symbolicNameVersion = helper.getBundleIdentity(newLocation);
        if (symbolicNameVersion == null || symbolicNameVersion[0] == null) {
            continue;
        }
        String sn = stripSymbolicName(symbolicNameVersion[0]);
        String vr = symbolicNameVersion[1];
        Version newVersion = VersionTable.getVersion(vr);
        Version updateableVersion = new Version(newVersion.getMajor(), newVersion.getMinor(), 0);
        // this bundle update from a patch may be applied only to relevant bundle|updateable-version, not to
        // *every* bundle with exact symbolic name
        String key = null;
        if (!coreBundles.containsKey(sn)) {
            key = String.format("%s|%s", sn, updateableVersion.toString());
        } else {
            key = sn;
        }
        // if existing bundle is within this range, update is possible
        VersionRange range = getUpdateableRange(patch, newLocation, newVersion);
        if (coreBundles.containsKey(sn)) {
            // so we lower down the lowest possible version of core bundle that we can update
            if (range == null) {
                range = new VersionRange(false, Version.emptyVersion, newVersion, true);
            } else {
                range = new VersionRange(false, Version.emptyVersion, range.getCeiling(), true);
            }
        } else if (range != null) {
            // if range is specified on non core bundle, the key should be different - updateable
            // version should be taken from range
            key = String.format("%s|%s", sn, range.getFloor().toString());
        }
        Bundle bundle = updateNotRequired.get(key);
        if (bundle == null && coreBundles.containsKey(sn)) {
            bundle = updateNotRequired.get(sn);
        }
        if (bundle == null || range == null) {
            // this patch ships a bundle that can't be used as an update for ANY currently installed bundle
            if (kind == PatchKind.NON_ROLLUP) {
                // which is strange, because non rollup patches should update existing bundles...
                if (range == null) {
                    System.err.printf("Skipping bundle %s - unable to process bundle without a version range configuration%n", newLocation);
                } else {
                // range is fine, we simply didn't find installed bundle at all - bundle from patch
                // will be stored in ${karaf.default.repository}, but not used as an update
                }
            }
            continue;
        }
        Version oldVersion = bundle.getVersion();
        if (range.contains(oldVersion)) {
            String oldLocation = history.getLocation(bundle);
            if ("org.ops4j.pax.url.mvn".equals(sn)) {
                Artifact artifact = Utils.mvnurlToArtifact(newLocation, true);
                if (artifact != null) {
                    URL location = new File(repository, String.format("org/ops4j/pax/url/pax-url-aether/%1$s/pax-url-aether-%1$s.jar", artifact.getVersion())).toURI().toURL();
                    newLocation = location.toString();
                }
            }
            int startLevel = bundle.adapt(BundleStartLevel.class).getStartLevel();
            int state = bundle.getState();
            BundleUpdate update = new BundleUpdate(sn, newVersion.toString(), newLocation, oldVersion.toString(), oldLocation, startLevel, state);
            if (bundleKeysFromFeatures.contains(key) || coreBundles.containsKey(sn)) {
                update.setIndependent(false);
            }
            updatesInThisPatch.add(update);
            updateNotRequired.remove(key);
            if (coreBundles.containsKey(sn)) {
                updateNotRequired.remove(sn);
            }
            // Merge result
            BundleUpdate oldUpdate = updatesForBundleKeys.get(key);
            if (oldUpdate != null) {
                Version upv = null;
                if (oldUpdate.getNewVersion() != null) {
                    upv = VersionTable.getVersion(oldUpdate.getNewVersion());
                }
                if (upv == null || upv.compareTo(newVersion) < 0) {
                    // other patch contains newer update for a bundle
                    updatesForBundleKeys.put(key, update);
                    bundleUpdateLocations.put(bundle, newLocation);
                }
            } else {
                // this is the first update of the bundle
                updatesForBundleKeys.put(key, update);
                bundleUpdateLocations.put(bundle, newLocation);
            }
        }
    }
    if (kind == PatchKind.ROLLUP) {
        // user features) and we have (at least try) to install them after restart.
        for (Bundle b : updateNotRequired.values()) {
            if (b.getSymbolicName() == null) {
                continue;
            }
            String symbolicName = stripSymbolicName(b.getSymbolicName());
            Version v = b.getVersion();
            Version updateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
            String key = String.format("%s|%s", symbolicName, updateableVersion.toString());
            int startLevel = b.adapt(BundleStartLevel.class).getStartLevel();
            int state = b.getState();
            BundleUpdate update = new BundleUpdate(symbolicName, null, null, v.toString(), history.getLocation(b), startLevel, state);
            if (bundleKeysFromFeatures.contains(key) || coreBundles.containsKey(symbolicName)) {
                // we don't have to install it separately
                update.setIndependent(false);
            }
            updatesInThisPatch.add(update);
            updatesForBundleKeys.put(key, update);
        }
    }
    return updatesInThisPatch;
}
Also used : BundleStartLevel(org.osgi.framework.startlevel.BundleStartLevel) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Bundle(org.osgi.framework.Bundle) Conditional(org.apache.karaf.features.Conditional) VersionRange(org.apache.felix.utils.version.VersionRange) Feature(org.apache.karaf.features.Feature) LinkedList(java.util.LinkedList) Utils.mvnurlToArtifact(io.fabric8.patch.management.Utils.mvnurlToArtifact) Artifact(io.fabric8.patch.management.Artifact) URL(java.net.URL) LinkedHashMap(java.util.LinkedHashMap) BundleInfo(org.apache.karaf.features.BundleInfo) Version(org.osgi.framework.Version) File(java.io.File) BundleUpdate(io.fabric8.patch.management.BundleUpdate) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) FeatureUpdate(io.fabric8.patch.management.FeatureUpdate)

Example 2 with FeatureUpdate

use of io.fabric8.patch.management.FeatureUpdate in project fabric8 by jboss-fuse.

the class ServiceImpl method install.

/**
 * <p>Main installation method. Installing a patch in non-fabric mode is a matter of correct merge (cherry-pick, merge,
 * rebase) of patch branch into <code>master</code> branch.</p>
 * <p>Static changes are handled by git, runtime changes (bundles, features) are handled depending on patch type:<ul>
 *     <li>Rollup: clear OSGi bundle cache, reinstall features that were installed after restart</li>
 *     <li>Non-Rollup: update bundles, generate overrides.properties and update scripts to reference new versions</li>
 * </ul></p>
 * <p>For Rollup patches we don't update bundles - we clear the bundle cache instead.</p>
 * @param patches
 * @param simulate
 * @param synchronous
 * @return
 */
private Map<String, PatchResult> install(final Collection<Patch> patches, final boolean simulate, boolean synchronous) {
    PatchKind kind = checkConsistency(patches);
    checkPrerequisites(patches);
    checkStandaloneChild(patches);
    // checkFabric();
    String transaction = null;
    try {
        // Compute individual patch results (patchId -> Result)
        final Map<String, PatchResult> results = new LinkedHashMap<String, PatchResult>();
        // current state of the framework
        Bundle[] allBundles = bundleContext.getBundles();
        // bundle -> url to update the bundle from (used for non-rollup patch)
        final Map<Bundle, String> bundleUpdateLocations = new HashMap<>();
        /* A "key" is name + "update'able version". Such version is current version with micro version == 0 */
        // [symbolic name|updateable-version] -> newest update for the bundle out of all installed patches
        final Map<String, BundleUpdate> updatesForBundleKeys = new LinkedHashMap<>();
        // [feature name|updateable-version] -> newest update for the feature out of all installed patches
        final Map<String, FeatureUpdate> updatesForFeatureKeys = new LinkedHashMap<>();
        // symbolic name -> version -> location
        final BundleVersionHistory history = createBundleVersionHistory();
        // beginning installation transaction = creating of temporary branch in git
        transaction = this.patchManagement.beginInstallation(kind);
        // bundles from etc/startup.properties + felix.framework = all bundles not managed by features
        // these bundles will be treated in special way
        // symbolic name -> Bundle
        final Map<String, Bundle> coreBundles = helper.getCoreBundles(allBundles);
        // runtime info is prepared to apply runtime changes and static info is prepared to update KARAF_HOME files
        for (Patch patch : patches) {
            List<FeatureUpdate> featureUpdatesInThisPatch = null;
            if (kind == PatchKind.ROLLUP) {
                // list of feature updates for the current patch
                featureUpdatesInThisPatch = featureUpdatesInPatch(patch, updatesForFeatureKeys, kind);
                helper.sortFeatureUpdates(featureUpdatesInThisPatch);
            }
            // list of bundle updates for the current patch - for ROLLUP patch, we minimize the list of bundles
            // to "restore" (install after clearing data/cache) by not including bundles that are
            // already updated as part of fueatures update
            List<BundleUpdate> bundleUpdatesInThisPatch = bundleUpdatesInPatch(patch, allBundles, bundleUpdateLocations, history, updatesForBundleKeys, kind, coreBundles, featureUpdatesInThisPatch);
            // each patch may change files, we're not updating the main files yet - it'll be done when
            // install transaction is committed
            patchManagement.install(transaction, patch, bundleUpdatesInThisPatch);
            // each patch may ship a migrator
            if (!simulate) {
                installMigratorBundle(patch);
            }
            // prepare patch result before doing runtime changes
            PatchResult result = null;
            if (patch.getResult() != null) {
                result = patch.getResult();
                if (patchManagement.isStandaloneChild()) {
                    // ENTESB-5120: "result" is actually a result of patch installation in root container
                    // we need dedicated result for admin:create based child container
                    PatchResult childResult = new PatchResult(patch.getPatchData(), simulate, System.currentTimeMillis(), bundleUpdatesInThisPatch, featureUpdatesInThisPatch, result);
                    result.addChildResult(System.getProperty("karaf.name"), childResult);
                }
            } else {
                result = new PatchResult(patch.getPatchData(), simulate, System.currentTimeMillis(), bundleUpdatesInThisPatch, featureUpdatesInThisPatch);
            }
            result.getKarafBases().add(String.format("%s | %s", System.getProperty("karaf.name"), System.getProperty("karaf.base")));
            results.put(patch.getPatchData().getId(), result);
        }
        // One special case
        if (kind == PatchKind.NON_ROLLUP) {
            // for rollup patch, this bundle will be installed from scratch
            for (Map.Entry<Bundle, String> entry : bundleUpdateLocations.entrySet()) {
                Bundle bundle = entry.getKey();
                if (bundle.getSymbolicName() != null && "org.ops4j.pax.url.mvn".equals(stripSymbolicName(bundle.getSymbolicName()))) {
                    // handle this bundle specially - update it here
                    URL location = new URL(entry.getValue());
                    System.out.printf("Special update of bundle \"%s\" from \"%s\"%n", bundle.getSymbolicName(), location);
                    if (!simulate) {
                        BundleUtils.update(bundle, location);
                        bundle.start();
                    }
                    // replace location - to be stored in result
                    bundleUpdateLocations.put(bundle, location.toString());
                }
            }
        }
        Presentation.displayFeatureUpdates(updatesForFeatureKeys.values(), true);
        // effectively, we will update all the bundles from this list - even if some bundles will be "updated"
        // as part of feature installation
        Presentation.displayBundleUpdates(updatesForBundleKeys.values(), true);
        // then required repositories, features and bundles will be reinstalled
        if (kind == PatchKind.ROLLUP) {
            if (!simulate) {
                if (patches.size() == 1) {
                    Patch patch = patches.iterator().next();
                    PatchResult result = results.get(patch.getPatchData().getId());
                    patch.setResult(result);
                    // single shot
                    if (patchManagement.isStandaloneChild()) {
                        backupService.backupDataFiles(result.getChildPatches().get(System.getProperty("karaf.name")), Pending.ROLLUP_INSTALLATION);
                    } else {
                        backupService.backupDataFiles(result, Pending.ROLLUP_INSTALLATION);
                    }
                    for (Bundle b : coreBundles.values()) {
                        if (b.getSymbolicName() != null && Utils.stripSymbolicName(b.getSymbolicName()).equals("org.apache.felix.fileinstall")) {
                            b.stop(Bundle.STOP_TRANSIENT);
                            break;
                        }
                    }
                    // update KARAF_HOME
                    patchManagement.commitInstallation(transaction);
                    if (patchManagement.isStandaloneChild()) {
                        result.getChildPatches().get(System.getProperty("karaf.name")).setPending(Pending.ROLLUP_INSTALLATION);
                    } else {
                        result.setPending(Pending.ROLLUP_INSTALLATION);
                    }
                    result.store();
                    // Some updates need a full JVM restart.
                    if (isJvmRestartNeeded(results)) {
                        boolean handlesFullRestart = Boolean.getBoolean("karaf.restart.jvm.supported");
                        if (handlesFullRestart) {
                            System.out.println("Rollup patch " + patch.getPatchData().getId() + " installed. Restarting Karaf..");
                            System.setProperty("karaf.restart.jvm", "true");
                        } else {
                            System.out.println("Rollup patch " + patch.getPatchData().getId() + " installed. Shutting down Karaf, please restart...");
                        }
                    } else {
                        // We don't need a JVM restart, so lets just do a OSGi framework restart
                        System.setProperty("karaf.restart", "true");
                    }
                    File karafData = new File(bundleContext.getProperty("karaf.data"));
                    File cleanCache = new File(karafData, "clean_cache");
                    cleanCache.createNewFile();
                    Thread.currentThread().setContextClassLoader(bundleContext.getBundle(0l).adapt(BundleWiring.class).getClassLoader());
                    bundleContext.getBundle(0l).stop();
                }
            } else {
                System.out.println("Simulation only - no files and runtime data will be modified.");
                patchManagement.rollbackInstallation(transaction);
            }
            return results;
        }
        // update KARAF_HOME
        if (!simulate) {
            patchManagement.commitInstallation(transaction);
        } else {
            patchManagement.rollbackInstallation(transaction);
        }
        if (!simulate) {
            Runnable task = new Runnable() {

                @Override
                public void run() {
                    try {
                        // update bundles
                        applyChanges(bundleUpdateLocations);
                        // persist results of all installed patches
                        for (Patch patch : patches) {
                            PatchResult result = results.get(patch.getPatchData().getId());
                            patch.setResult(result);
                            result.store();
                        }
                    } catch (Exception e) {
                        e.printStackTrace(System.err);
                        System.err.flush();
                    }
                }
            };
            if (synchronous) {
                task.run();
            } else {
                new Thread(task).start();
            }
        } else {
            System.out.println("Simulation only - no files and runtime data will be modified.");
        }
        return results;
    } catch (Exception e) {
        e.printStackTrace(System.err);
        System.err.flush();
        if (transaction != null && patchManagement != null) {
            patchManagement.rollbackInstallation(transaction);
        }
        throw new PatchException(e.getMessage(), e);
    } finally {
        System.out.flush();
    }
}
Also used : HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) PatchKind(io.fabric8.patch.management.PatchKind) URL(java.net.URL) LinkedHashMap(java.util.LinkedHashMap) BundleUpdate(io.fabric8.patch.management.BundleUpdate) FeatureUpdate(io.fabric8.patch.management.FeatureUpdate) Bundle(org.osgi.framework.Bundle) URISyntaxException(java.net.URISyntaxException) PatchException(io.fabric8.patch.management.PatchException) BundleException(org.osgi.framework.BundleException) IOException(java.io.IOException) PatchResult(io.fabric8.patch.management.PatchResult) PatchException(io.fabric8.patch.management.PatchException) Patch(io.fabric8.patch.management.Patch) Map(java.util.Map) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) File(java.io.File)

Example 3 with FeatureUpdate

use of io.fabric8.patch.management.FeatureUpdate in project fabric8 by jboss-fuse.

the class ServiceImpl method featureUpdatesInPatch.

/**
 * Returns a list of {@link FeatureUpdate} for single patch, taking into account already discovered updates
 * @param patch
 * @param updatesForFeatureKeys
 * @param kind
 * @return
 */
private List<FeatureUpdate> featureUpdatesInPatch(Patch patch, Map<String, FeatureUpdate> updatesForFeatureKeys, PatchKind kind) throws Exception {
    Set<String> addedRepositoryNames = new HashSet<>();
    HashMap<String, Repository> after = null;
    try {
        List<FeatureUpdate> updatesInThisPatch = new LinkedList<>();
        /*
             * Two pairs of features makes feature names not enough to be a key:
             * <feature name="openjpa" description="Apache OpenJPA 2.2.x persistent engine support" version="2.2.2" resolver="(obr)">
             * <feature name="openjpa" description="Apache OpenJPA 2.3.x persistence engine support" version="2.3.0" resolver="(obr)">
             * and
             * <feature name="activemq-camel" version="5.11.0.redhat-621039" resolver="(obr)" start-level="50">
             * <feature name="activemq-camel" version="1.2.0.redhat-621039" resolver="(obr)">
             */
        // install the new feature repos, tracking the set the were
        // installed before and after
        // (e.g, "karaf-enterprise-2.4.0.redhat-620133" -> Repository)
        Map<String, Repository> before = new HashMap<>(getAvailableFeatureRepositories());
        for (String url : patch.getPatchData().getFeatureFiles()) {
            featuresService.addRepository(new URI(url));
        }
        after = getAvailableFeatureRepositories();
        // track which old repos provide which features to find out if we have new repositories for those features
        // key is name|version (don't expect '|' to be part of name...)
        // assume that [feature-name, feature-version{major,minor,0,0}] is defined only in single repository
        Map<String, String> featuresInOldRepositories = new HashMap<>();
        // key is only name, without version - used when there's single feature in old and in new repositories
        MultiMap<String, String> singleFeaturesInOldRepositories = new MultiMap<>();
        Map<String, Version> actualOldFeatureVersions = new HashMap<>();
        for (Repository existingRepository : before.values()) {
            for (Feature feature : existingRepository.getFeatures()) {
                Version v = Utils.getOsgiVersion(feature.getVersion());
                Version lowestUpdateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
                // assume that we can update feature XXX-2.2.3 to XXX-2.2.142, but not to XXX-2.3.0.alpha-1
                String key = String.format("%s|%s", feature.getName(), lowestUpdateableVersion.toString());
                featuresInOldRepositories.put(key, existingRepository.getURI().toString());
                singleFeaturesInOldRepositories.put(feature.getName(), existingRepository.getURI().toString());
                actualOldFeatureVersions.put(key, v);
            }
        }
        // Use the before and after set to figure out which repos were added.
        addedRepositoryNames = new HashSet<>(after.keySet());
        addedRepositoryNames.removeAll(before.keySet());
        // track the new repositories where we can find old features
        Map<String, String> featuresInNewRepositories = new HashMap<>();
        MultiMap<String, String> singleFeaturesInNewRepositories = new MultiMap<>();
        Map<String, String> actualNewFeatureVersions = new HashMap<>();
        MultiMap<String, String> singleActualNewFeatureVersions = new MultiMap<>();
        // Figure out which old repos were updated:  Do they have feature
        // with the same name as one contained in a repo being added?
        // and do they have update'able version? (just like with bundles)
        Set<String> oldRepositoryNames = new HashSet<String>();
        for (String addedRepositoryName : addedRepositoryNames) {
            Repository added = after.get(addedRepositoryName);
            for (Feature feature : added.getFeatures()) {
                Version v = Utils.getOsgiVersion(feature.getVersion());
                Version lowestUpdateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
                String key = String.format("%s|%s", feature.getName(), lowestUpdateableVersion.toString());
                featuresInNewRepositories.put(key, addedRepositoryName);
                singleFeaturesInNewRepositories.put(feature.getName(), addedRepositoryName);
                actualNewFeatureVersions.put(key, v.toString());
                singleActualNewFeatureVersions.put(feature.getName(), v.toString());
                String oldRepositoryWithUpdateableFeature = featuresInOldRepositories.get(key);
                if (oldRepositoryWithUpdateableFeature == null && singleFeaturesInOldRepositories.get(feature.getName()) != null && singleFeaturesInOldRepositories.get(feature.getName()).size() == 1) {
                    oldRepositoryWithUpdateableFeature = singleFeaturesInOldRepositories.get(feature.getName()).get(0);
                }
                if (oldRepositoryWithUpdateableFeature != null) {
                    // track the old repository to be removed
                    oldRepositoryNames.add(oldRepositoryWithUpdateableFeature);
                }
            }
        }
        // We need to uninstall them. Before we uninstall, track which features were installed.
        for (String oldRepositoryName : oldRepositoryNames) {
            Repository repository = before.get(oldRepositoryName);
            for (Feature feature : repository.getFeatures()) {
                if (featuresService.isInstalled(feature)) {
                    Version v = Utils.getOsgiVersion(feature.getVersion());
                    Version lowestUpdateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
                    String key = String.format("%s|%s", feature.getName(), lowestUpdateableVersion.toString());
                    String newRepositoryName = featuresInNewRepositories.get(key);
                    String newVersion = actualNewFeatureVersions.get(key);
                    if (newRepositoryName == null) {
                        // feature from 1.1.1 to 1.3.0
                        if (singleFeaturesInOldRepositories.get(feature.getName()) != null && singleFeaturesInOldRepositories.get(feature.getName()).size() == 1 && singleFeaturesInNewRepositories.get(feature.getName()) != null && singleFeaturesInNewRepositories.get(feature.getName()).size() == 1) {
                            newRepositoryName = singleFeaturesInNewRepositories.get(feature.getName()).get(0);
                        }
                    }
                    if (newVersion == null) {
                        if (singleActualNewFeatureVersions.get(feature.getName()) != null && singleActualNewFeatureVersions.get(feature.getName()).size() == 1) {
                            newVersion = singleActualNewFeatureVersions.get(feature.getName()).get(0);
                        }
                    }
                    FeatureUpdate featureUpdate = null;
                    if (newVersion != null && newRepositoryName != null) {
                        featureUpdate = new FeatureUpdate(feature.getName(), after.get(oldRepositoryName).getURI().toString(), feature.getVersion(), after.get(newRepositoryName).getURI().toString(), newVersion);
                    } else {
                        // we didn't find an update for installed features among feature repositories from patch
                        // which means we have to preserve both the feature and the repository - this may
                        // be user's feature
                        featureUpdate = new FeatureUpdate(feature.getName(), after.get(oldRepositoryName).getURI().toString(), feature.getVersion(), null, null);
                    }
                    updatesInThisPatch.add(featureUpdate);
                    // Merge result
                    FeatureUpdate oldUpdate = updatesForFeatureKeys.get(key);
                    if (oldUpdate != null) {
                        Version upv = null, newV = null;
                        if (oldUpdate.getNewVersion() != null) {
                            upv = VersionTable.getVersion(oldUpdate.getNewVersion());
                        }
                        if (newVersion != null) {
                            newV = VersionTable.getVersion(newVersion);
                        }
                        if (upv == null && newV == null) {
                        // weird...
                        } else {
                            if (upv == null || (newV != null && upv.compareTo(newV) < 0)) {
                                // other patch contains newer update for the feature
                                updatesForFeatureKeys.put(key, featureUpdate);
                            }
                        }
                    } else {
                        // this is the first update of the bundle
                        updatesForFeatureKeys.put(key, featureUpdate);
                    }
                }
            }
        }
        // now let's see if there are repositories that are NOT updated (either they're not available in patch
        // (like user feature repositories) or simply didn't change (like jclouds 1.8.1 between Fuse 6.2 and 6.2.1)
        Set<String> unchangedRepositoryNames = new HashSet<>(before.keySet());
        unchangedRepositoryNames.removeAll(oldRepositoryNames);
        for (String unchangedRepositoryName : unchangedRepositoryNames) {
            Repository repository = before.get(unchangedRepositoryName);
            boolean hasInstalledFeatures = false;
            for (Feature feature : repository.getFeatures()) {
                if (featuresService.isInstalled(feature)) {
                    FeatureUpdate featureUpdate = new FeatureUpdate(feature.getName(), after.get(unchangedRepositoryName).getURI().toString(), feature.getVersion(), null, null);
                    hasInstalledFeatures = true;
                    // preserve unchanged/user feature - install after restart
                    updatesInThisPatch.add(featureUpdate);
                    // the key doesn't matter
                    updatesForFeatureKeys.put(String.format("%s|%s", feature.getName(), feature.getVersion()), featureUpdate);
                }
            }
            if (!hasInstalledFeatures) {
                // we have to preserve unchanged/user feature repository - even if it had no installed features
                // this featureUpdate means - "restore feature repository only"
                FeatureUpdate featureUpdate = new FeatureUpdate(null, after.get(unchangedRepositoryName).getURI().toString(), null, null, null);
                updatesInThisPatch.add(featureUpdate);
                updatesForFeatureKeys.put(String.format("REPOSITORY_TO_ADD:%s", after.get(unchangedRepositoryName).getURI().toString()), featureUpdate);
            }
        }
        return updatesInThisPatch;
    } catch (Exception e) {
        throw new PatchException(e.getMessage(), e);
    } finally {
        // we'll add new feature repositories again later. here we've added them only to track the updates
        if (after != null) {
            for (String repo : addedRepositoryNames) {
                if (after.get(repo) != null) {
                    featuresService.removeRepository(after.get(repo).getURI(), false);
                }
            }
        }
    }
}
Also used : HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) URI(java.net.URI) Feature(org.apache.karaf.features.Feature) LinkedList(java.util.LinkedList) URISyntaxException(java.net.URISyntaxException) PatchException(io.fabric8.patch.management.PatchException) BundleException(org.osgi.framework.BundleException) IOException(java.io.IOException) Repository(org.apache.karaf.features.Repository) Version(org.osgi.framework.Version) PatchException(io.fabric8.patch.management.PatchException) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) FeatureUpdate(io.fabric8.patch.management.FeatureUpdate)

Example 4 with FeatureUpdate

use of io.fabric8.patch.management.FeatureUpdate in project fabric8 by jboss-fuse.

the class ServiceImpl method resumePendingPatchTasks.

/**
 * Upon startup (activation), we check if there are any *.patch.pending files. if yes, we're finishing the
 * installation
 */
private void resumePendingPatchTasks() throws IOException {
    File[] pendingPatches = patchDir.listFiles(new FileFilter() {

        @Override
        public boolean accept(File pathname) {
            return pathname.exists() && pathname.getName().endsWith(".pending");
        }
    });
    if (pendingPatches == null || pendingPatches.length == 0) {
        return;
    }
    for (File pending : pendingPatches) {
        Pending what = Pending.valueOf(FileUtils.readFileToString(pending));
        String name = pending.getName().replaceFirst("\\.pending$", "");
        if (patchManagement.isStandaloneChild()) {
            if (name.endsWith("." + System.getProperty("karaf.name") + ".patch")) {
                name = name.replaceFirst("\\." + System.getProperty("karaf.name"), "");
            } else {
                continue;
            }
        }
        File patchFile = new File(pending.getParentFile(), name);
        if (!patchFile.isFile()) {
            System.out.println("Ignoring patch result file: " + patchFile.getName());
            continue;
        }
        PatchData patchData = PatchData.load(new FileInputStream(patchFile));
        Patch patch = patchManagement.loadPatch(new PatchDetailsRequest(patchData.getId()));
        System.out.printf("Resume %s of %spatch \"%s\"%n", what == Pending.ROLLUP_INSTALLATION ? "installation" : "rollback", patch.getPatchData().isRollupPatch() ? "rollup " : "", patch.getPatchData().getId());
        PatchResult result = patch.getResult();
        if (patchManagement.isStandaloneChild()) {
            result = result.getChildPatches().get(System.getProperty("karaf.name"));
            if (result == null) {
                System.out.println("Ignoring patch result file: " + patchFile.getName());
                continue;
            }
        }
        // feature time
        Set<String> newRepositories = new LinkedHashSet<>();
        Set<String> features = new LinkedHashSet<>();
        for (FeatureUpdate featureUpdate : result.getFeatureUpdates()) {
            if (featureUpdate.getName() == null && featureUpdate.getPreviousRepository() != null) {
                // feature was not shipped by patch
                newRepositories.add(featureUpdate.getPreviousRepository());
            } else if (featureUpdate.getNewRepository() == null) {
                // feature was not changed by patch
                newRepositories.add(featureUpdate.getPreviousRepository());
                features.add(String.format("%s|%s", featureUpdate.getName(), featureUpdate.getPreviousVersion()));
            } else {
                // feature was shipped by patch
                if (what == Pending.ROLLUP_INSTALLATION) {
                    newRepositories.add(featureUpdate.getNewRepository());
                    features.add(String.format("%s|%s", featureUpdate.getName(), featureUpdate.getNewVersion()));
                } else {
                    newRepositories.add(featureUpdate.getPreviousRepository());
                    features.add(String.format("%s|%s", featureUpdate.getName(), featureUpdate.getPreviousVersion()));
                }
            }
        }
        for (String repo : newRepositories) {
            System.out.println("Restoring feature repository: " + repo);
            try {
                featuresService.addRepository(URI.create(repo));
            } catch (Exception e) {
                System.err.println(e.getMessage());
                e.printStackTrace(System.err);
                System.err.flush();
            }
        }
        for (String f : features) {
            String[] fv = f.split("\\|");
            System.out.printf("Restoring feature %s/%s%n", fv[0], fv[1]);
            try {
                featuresService.installFeature(fv[0], fv[1]);
            } catch (Exception e) {
                System.err.println(e.getMessage());
                e.printStackTrace(System.err);
                System.err.flush();
            }
        }
        for (BundleUpdate update : result.getBundleUpdates()) {
            if (!update.isIndependent()) {
                continue;
            }
            String location = null;
            if (update.getNewVersion() == null) {
                System.out.printf("Restoring bundle %s from %s%n", update.getSymbolicName(), update.getPreviousLocation());
                location = update.getPreviousLocation();
            } else {
                if (what == Pending.ROLLUP_INSTALLATION) {
                    System.out.printf("Updating bundle %s from %s%n", update.getSymbolicName(), update.getNewLocation());
                    location = update.getNewLocation();
                } else {
                    System.out.printf("Downgrading bundle %s from %s%n", update.getSymbolicName(), update.getPreviousLocation());
                    location = update.getPreviousLocation();
                }
            }
            try {
                Bundle b = bundleContext.installBundle(location);
                if (update.getStartLevel() > -1) {
                    b.adapt(BundleStartLevel.class).setStartLevel(update.getStartLevel());
                }
                switch(update.getState()) {
                    // ?
                    case Bundle.UNINSTALLED:
                    case Bundle.INSTALLED:
                    case Bundle.STARTING:
                    case Bundle.STOPPING:
                        break;
                    case Bundle.RESOLVED:
                        // ?bundleContext.getBundle(0L).adapt(org.osgi.framework.wiring.FrameworkWiring.class).resolveBundles(...);
                        break;
                    case Bundle.ACTIVE:
                        b.start();
                        break;
                }
            } catch (BundleException e) {
                System.err.println(" - " + e.getMessage());
                // e.printStackTrace(System.err);
                System.err.flush();
            }
        }
        pending.delete();
        System.out.printf("%spatch \"%s\" %s successfully%n", patch.getPatchData().isRollupPatch() ? "Rollup " : "", patchData.getId(), what == Pending.ROLLUP_INSTALLATION ? "installed" : "rolled back");
        if (what == Pending.ROLLUP_ROLLBACK) {
            List<String> bases = patch.getResult().getKarafBases();
            for (Iterator<String> iterator = bases.iterator(); iterator.hasNext(); ) {
                String s = iterator.next();
                if (s.startsWith(System.getProperty("karaf.name"))) {
                    iterator.remove();
                }
            }
            result.setPending(null);
            patch.getResult().store();
            if (patch.getResult().getKarafBases().size() == 0) {
                File file = new File(patchDir, patchData.getId() + ".patch.result");
                file.delete();
            }
            if (patchManagement.isStandaloneChild()) {
                File file = new File(patchDir, patchData.getId() + "." + System.getProperty("karaf.name") + ".patch.result");
                if (file.isFile()) {
                    file.delete();
                }
            }
        }
    }
}
Also used : LinkedHashSet(java.util.LinkedHashSet) BundleStartLevel(org.osgi.framework.startlevel.BundleStartLevel) PatchData(io.fabric8.patch.management.PatchData) Bundle(org.osgi.framework.Bundle) PatchDetailsRequest(io.fabric8.patch.management.PatchDetailsRequest) FileInputStream(java.io.FileInputStream) URISyntaxException(java.net.URISyntaxException) PatchException(io.fabric8.patch.management.PatchException) BundleException(org.osgi.framework.BundleException) IOException(java.io.IOException) PatchResult(io.fabric8.patch.management.PatchResult) BundleException(org.osgi.framework.BundleException) FileFilter(java.io.FileFilter) File(java.io.File) Patch(io.fabric8.patch.management.Patch) Pending(io.fabric8.patch.management.Pending) BundleUpdate(io.fabric8.patch.management.BundleUpdate) FeatureUpdate(io.fabric8.patch.management.FeatureUpdate)

Example 5 with FeatureUpdate

use of io.fabric8.patch.management.FeatureUpdate in project fabric8 by jboss-fuse.

the class OSGiPatchHelper method sortFeatureUpdates.

/**
 * Sorts feature updates in the way that features listed in etc/org.apache.karaf.features.cfg:featuresBoot
 * are updated first
 * @param featureUpdatesInThisPatch
 */
public void sortFeatureUpdates(List<FeatureUpdate> featureUpdatesInThisPatch) throws IOException {
    Properties props = new Properties();
    FileInputStream stream = new FileInputStream(new File(karafHome, "etc/org.apache.karaf.features.cfg"));
    props.load(stream);
    IOUtils.closeQuietly(stream);
    String p = props.getProperty("featuresBoot");
    if (p != null) {
        String[] properties = p.split("\\s,\\s");
        Set<String> featuresBoot = new LinkedHashSet<>(Arrays.asList(properties));
        // Set<String> featuresSpecial = new LinkedHashSet<>(Arrays.asList("cxf-specs", "cxf-core", "cxf-jaxrs"));
        Set<String> featuresSpecial = new LinkedHashSet<>(Arrays.asList("fabric-cxf"));
        Map<String, FeatureUpdate> newOrderedUpdates = new HashMap<>();
        List<FeatureUpdate> newOtherUpdates = new LinkedList<>();
        List<FeatureUpdate> newUpdates = new LinkedList<>();
        for (Iterator<FeatureUpdate> iterator = featureUpdatesInThisPatch.iterator(); iterator.hasNext(); ) {
            FeatureUpdate update = iterator.next();
            if (update.getName() != null && (featuresBoot.contains(update.getName()) || featuresSpecial.contains(update.getName()))) {
                newOrderedUpdates.put(update.getName(), update);
            } else {
                newOtherUpdates.add(update);
            }
        }
        for (String fb : featuresBoot) {
            if (newOrderedUpdates.containsKey(fb)) {
                newUpdates.add(newOrderedUpdates.get(fb));
            }
        }
        for (String fs : featuresSpecial) {
            if (newOrderedUpdates.containsKey(fs)) {
                newUpdates.add(newOrderedUpdates.get(fs));
            }
        }
        newUpdates.addAll(newOtherUpdates);
        featureUpdatesInThisPatch.clear();
        featureUpdatesInThisPatch.addAll(newUpdates);
    }
}
Also used : LinkedHashSet(java.util.LinkedHashSet) HashMap(java.util.HashMap) Properties(java.util.Properties) FileInputStream(java.io.FileInputStream) LinkedList(java.util.LinkedList) File(java.io.File) FeatureUpdate(io.fabric8.patch.management.FeatureUpdate)

Aggregations

FeatureUpdate (io.fabric8.patch.management.FeatureUpdate)6 File (java.io.File)4 HashMap (java.util.HashMap)4 LinkedHashSet (java.util.LinkedHashSet)4 BundleUpdate (io.fabric8.patch.management.BundleUpdate)3 PatchException (io.fabric8.patch.management.PatchException)3 IOException (java.io.IOException)3 URISyntaxException (java.net.URISyntaxException)3 LinkedHashMap (java.util.LinkedHashMap)3 LinkedList (java.util.LinkedList)3 Bundle (org.osgi.framework.Bundle)3 BundleException (org.osgi.framework.BundleException)3 Patch (io.fabric8.patch.management.Patch)2 PatchResult (io.fabric8.patch.management.PatchResult)2 FileInputStream (java.io.FileInputStream)2 URL (java.net.URL)2 HashSet (java.util.HashSet)2 Feature (org.apache.karaf.features.Feature)2 Version (org.osgi.framework.Version)2 BundleStartLevel (org.osgi.framework.startlevel.BundleStartLevel)2