use of org.jboss.fuse.patch.management.PatchException in project fuse-karaf by jboss-fuse.
the class PatchServiceImpl method rollback.
@Override
public void rollback(final Patch patch, boolean simulate, boolean force) throws PatchException {
final PatchResult result = !patchManagement.isStandaloneChild() ? patch.getResult() : patch.getResult().getChildPatches().get(System.getProperty("karaf.name"));
if (result == null) {
throw new PatchException("Patch " + patch.getPatchData().getId() + " is not installed");
}
if (patch.getPatchData().isRollupPatch()) {
// we already have the "state" (feature repositories, features, bundles and their states, datafiles
// and start-level info) stored in *.result file
Presentation.displayFeatureUpdates(result.getFeatureUpdates(), false);
Presentation.displayBundleUpdates(result.getBundleUpdates(), false);
try {
if (!simulate) {
// let's backup data files before configadmin detects changes to etc/* files.
backupService.backupDataFiles(result, Pending.ROLLUP_ROLLBACK);
for (Bundle b : this.bundleContext.getBundles()) {
if (b.getSymbolicName() != null && Utils.stripSymbolicName(b.getSymbolicName()).equals("org.apache.felix.fileinstall")) {
b.stop(Bundle.STOP_TRANSIENT);
break;
}
}
patchManagement.rollback(patch.getPatchData());
result.setPending(Pending.ROLLUP_ROLLBACK);
if (patchManagement.isStandaloneChild()) {
result.getParent().store();
} else {
result.store();
}
if (isJvmRestartNeeded(result)) {
boolean handlesFullRestart = Boolean.getBoolean("karaf.restart.jvm.supported");
if (handlesFullRestart) {
System.out.println("Rollup patch " + patch.getPatchData().getId() + " rolled back. Restarting Karaf..");
// KARAF-5179 - we need both properties set to true
System.setProperty("karaf.restart", "true");
System.setProperty("karaf.restart.jvm", "true");
} else {
System.out.println("Rollup patch " + patch.getPatchData().getId() + " rolled back. 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();
// stop/shutdown occurs on another thread
return;
} else {
System.out.println("Simulation only - no files and runtime data will be modified.");
return;
}
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.flush();
throw new PatchException(e.getMessage(), e);
}
}
// continue with NON_ROLLUP patch
// current state of the framework
Bundle[] allBundles = bundleContext.getBundles();
// check if all the bundles that were updated in patch are available (installed)
List<BundleUpdate> badUpdates = new ArrayList<BundleUpdate>();
for (BundleUpdate update : result.getBundleUpdates()) {
boolean found = false;
Version v = Version.parseVersion(update.getNewVersion() == null ? update.getPreviousVersion() : update.getNewVersion());
for (Bundle bundle : allBundles) {
if (bundle.getSymbolicName() == null || update.getSymbolicName() == null) {
continue;
}
if (stripSymbolicName(bundle.getSymbolicName()).equals(stripSymbolicName(update.getSymbolicName())) && bundle.getVersion().equals(v)) {
found = true;
break;
}
}
if (!found) {
badUpdates.add(update);
}
}
if (!badUpdates.isEmpty() && !force) {
StringBuilder sb = new StringBuilder();
sb.append("Unable to rollback patch ").append(patch.getPatchData().getId()).append(" because of the following missing bundles:\n");
for (BundleUpdate up : badUpdates) {
String version = up.getNewVersion() == null ? up.getPreviousVersion() : up.getNewVersion();
sb.append(" - ").append(up.getSymbolicName()).append("/").append(version).append("\n");
}
throw new PatchException(sb.toString());
}
if (!simulate) {
// bundle -> old location of the bundle to downgrade from
final Map<Bundle, String> toUpdate = new HashMap<Bundle, String>();
for (BundleUpdate update : result.getBundleUpdates()) {
Version v = Version.parseVersion(update.getNewVersion() == null ? update.getPreviousVersion() : update.getNewVersion());
for (Bundle bundle : allBundles) {
if (bundle.getSymbolicName() == null || update.getSymbolicName() == null) {
continue;
}
if (stripSymbolicName(bundle.getSymbolicName()).equals(stripSymbolicName(update.getSymbolicName())) && bundle.getVersion().equals(v)) {
toUpdate.put(bundle, update.getPreviousLocation());
}
}
}
final boolean isStandaloneChild = patchManagement.isStandaloneChild();
patchManagement.rollback(patch.getPatchData());
Executors.newSingleThreadExecutor().execute(() -> {
try {
applyChanges(toUpdate);
for (String featureOverride : result.getFeatureOverrides()) {
System.out.println("removing overriden feature: " + featureOverride);
}
if (result.getFeatureOverrides().size() > 0) {
System.out.println("refreshing features");
featuresService.refreshFeatures(EnumSet.noneOf(FeaturesService.Option.class));
}
} catch (Exception e) {
throw new PatchException("Unable to rollback patch " + patch.getPatchData().getId() + ": " + e.getMessage(), e);
}
try {
List<String> bases = patch.getResult().getKarafBases();
bases.removeIf(s -> s.startsWith(System.getProperty("karaf.name")));
patch.getResult().store();
if (patch.getResult().getKarafBases().size() == 0) {
File file = new File(patchDir, result.getPatchData().getId() + ".patch.result");
file.delete();
patch.setResult(null);
}
if (patchManagement.isStandaloneChild()) {
File file = new File(patchDir, result.getPatchData().getId() + "." + System.getProperty("karaf.name") + ".patch.result");
if (file.isFile()) {
file.delete();
}
}
} catch (Exception e) {
LOG.warn("Problem updating metadata for patch \"" + patch.getPatchData().getId() + "\": " + e.getMessage());
}
});
}
}
use of org.jboss.fuse.patch.management.PatchException in project fuse-karaf by jboss-fuse.
the class PatchServiceImpl method checkStandaloneChild.
/**
* Check if this is installation in @{link {@link org.jboss.fuse.patch.management.EnvType#STANDALONE_CHILD}}
* - in this case the patch has to be installed in root first
* @param patches
*/
private void checkStandaloneChild(Collection<Patch> patches) {
if (patchManagement.isStandaloneChild()) {
for (Patch patch : patches) {
if (patch.getResult() == null) {
throw new PatchException(String.format("Patch '%s' should be installed in parent container first", patch.getPatchData().getId()));
} else {
List<String> bases = patch.getResult().getKarafBases();
boolean isInstalledInRoot = false;
for (String base : bases) {
String[] coords = base.split("\\s*\\|\\s*");
if (coords.length == 2 && coords[1].trim().equals(System.getProperty("karaf.home"))) {
isInstalledInRoot = true;
}
}
if (!isInstalledInRoot) {
throw new PatchException(String.format("Patch '%s' should be installed in parent container first", patch.getPatchData().getId()));
}
}
}
}
}
use of org.jboss.fuse.patch.management.PatchException in project fuse-karaf by jboss-fuse.
the class PatchServiceImpl method download.
@Override
public Iterable<Patch> download(URL url) {
if ("file".equals(url.getProtocol())) {
// ENTESB-4992: prevent adding non existing files or directories
try {
if (!new File(url.toURI()).isFile()) {
throw new PatchException("Path " + url.getPath() + " doesn't exist or is not a file");
}
} catch (URISyntaxException e) {
throw new PatchException(e.getMessage(), e);
}
}
try {
List<PatchData> patchesData = patchManagement.fetchPatches(url);
List<Patch> patches = new ArrayList<>(patchesData.size());
for (PatchData patchData : patchesData) {
Patch patch = patchManagement.trackPatch(patchData);
patches.add(patch);
}
return patches;
} catch (PatchException e) {
throw e;
} catch (Exception e) {
throw new PatchException("Unable to download patch from url " + url, e);
}
}
use of org.jboss.fuse.patch.management.PatchException in project fuse-karaf by jboss-fuse.
the class PatchServiceImpl 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;
Version 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);
}
}
}
}
}
use of org.jboss.fuse.patch.management.PatchException in project fuse-karaf by jboss-fuse.
the class PatchServiceImplTest method testCheckPrerequisitesNotInstalled.
@Test
public void testCheckPrerequisitesNotInstalled() throws IOException {
PatchServiceImpl service = createMockServiceImpl(getDirectoryForResource("prereq/patch2.patch"));
Patch patch = service.getPatch("patch2");
assertNotNull(patch);
try {
service.checkPrerequisites(patch);
fail("Patch will prerequisites that are not yet installed should not pass check");
} catch (PatchException e) {
assertTrue(e.getMessage().toLowerCase().contains("required patch 'prereq2' is not installed"));
}
}
Aggregations