You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2017/10/28 10:02:57 UTC
[3/7] brooklyn-server git commit: PR #867: incorporate review comments
PR #867: incorporate review comments
And better handle pre-existing bundles.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/85196899
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/85196899
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/85196899
Branch: refs/heads/master
Commit: 85196899f1a7b151767623f3f735c6d155dd5e28
Parents: 2a539c0
Author: Aled Sage <al...@gmail.com>
Authored: Tue Oct 24 13:50:01 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Fri Oct 27 13:49:55 2017 +0100
----------------------------------------------------------------------
.../rebind/mementos/ManagedBundleMemento.java | 5 +
.../camp/brooklyn/AbstractYamlTest.java | 2 +-
.../catalog/CatalogOsgiYamlEntityTest.java | 2 +-
.../catalog/CatalogOsgiYamlVersioningTest.java | 2 +-
.../brooklyn/test/lite/CampYamlLiteTest.java | 2 +-
.../catalog/internal/BasicBrooklynCatalog.java | 2 +-
.../core/mgmt/ha/OsgiArchiveInstaller.java | 241 +++++++++-----
.../core/mgmt/rebind/RebindIteration.java | 2 +-
.../rebind/dto/BasicManagedBundleMemento.java | 20 +-
.../core/typereg/BasicManagedBundle.java | 20 +-
.../AbstractBrooklynLauncherRebindTest.java | 30 +-
.../BrooklynLauncherRebindCatalogOsgiTest.java | 321 ++++++++++++++++---
12 files changed, 494 insertions(+), 155 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/ManagedBundleMemento.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/ManagedBundleMemento.java b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/ManagedBundleMemento.java
index 7e487ff..ffcd783 100644
--- a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/ManagedBundleMemento.java
+++ b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/ManagedBundleMemento.java
@@ -18,6 +18,8 @@
*/
package org.apache.brooklyn.api.mgmt.rebind.mementos;
+import javax.annotation.Nullable;
+
import com.google.common.io.ByteSource;
public interface ManagedBundleMemento extends Memento {
@@ -27,6 +29,9 @@ public interface ManagedBundleMemento extends Memento {
String getUrl();
+ @Nullable
+ String getChecksum();
+
ByteSource getJarContent();
void setJarContent(ByteSource byteSource);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
index 3d4bbd2..091598f 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -263,7 +263,7 @@ public abstract class AbstractYamlTest {
File bf = bundleMaker.createTempZip("test", MutableMap.of(
new ZipEntry(BasicBrooklynCatalog.CATALOG_BOM), new ByteArrayInputStream(catalogYaml.getBytes())));
ReferenceWithError<OsgiBundleInstallationResult> b = ((ManagementContextInternal)mgmt).getOsgiManager().get().installDeferredStart(
- new BasicManagedBundle(bundleName.getSymbolicName(), bundleName.getVersionString(), null),
+ new BasicManagedBundle(bundleName.getSymbolicName(), bundleName.getVersionString(), null, null),
new FileInputStream(bf),
false);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
index 6d7df19..ae0df91 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
@@ -333,7 +333,7 @@ public class CatalogOsgiYamlEntityTest extends AbstractYamlTest {
addCatalogOSGiEntity(id, SIMPLE_ENTITY_TYPE, true);
Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
- Asserts.expectedFailureContainsIgnoreCase(e, id, "already installed", "cannot install a different bundle at a same non-snapshot version");
+ Asserts.expectedFailureContainsIgnoreCase(e, id, "already installed", "cannot install a different bundle with the same non-snapshot version");
}
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlVersioningTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlVersioningTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlVersioningTest.java
index 672c722..9042663 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlVersioningTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlVersioningTest.java
@@ -74,7 +74,7 @@ public class CatalogOsgiYamlVersioningTest extends CatalogYamlVersioningTest {
@Override
protected void checkAddSameVersionFailsWhenIconIsDifferent(Exception e) {
Asserts.expectedFailureContainsIgnoreCase(e,
- "cannot install a different bundle at a same non-snapshot version");
+ "cannot install a different bundle with the same non-snapshot version");
assertExpectedFailureIncludesSampleId(e);
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
index 92b053e..d4b67c5 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
@@ -213,7 +213,7 @@ public class CampYamlLiteTest {
// install bundle for class access but without loading its catalog.bom,
// since we only have mock matchers here
// (if we don't do this, the default routines install it and try to process the catalog.bom, failing)
- ((ManagementContextInternal)mgmt).getOsgiManager().get().installDeferredStart(new BasicManagedBundle(null, null, bundleUrl), null, false).get();
+ ((ManagementContextInternal)mgmt).getOsgiManager().get().installDeferredStart(new BasicManagedBundle(null, null, bundleUrl, null), null, false).get();
}
private void assertMgmtHasSampleMyCatalogApp(String symbolicName, String bundleUrl) {
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index 810ffc1..c1c893c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -1503,7 +1503,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
OsgiBundleInstallationResult result = null;
try {
- result = osgiManager.install(new BasicManagedBundle(vn.getSymbolicName(), vn.getVersionString(), null), new FileInputStream(bf), true, true, forceUpdate).get();
+ result = osgiManager.install(new BasicManagedBundle(vn.getSymbolicName(), vn.getVersionString(), null, null), new FileInputStream(bf), true, true, forceUpdate).get();
} catch (FileNotFoundException e) {
throw Exceptions.propagate(e);
} finally {
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
index 968fbf9..e3b91fa 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
@@ -26,6 +26,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.Attributes;
@@ -362,6 +363,7 @@ class OsgiArchiveInstaller {
final boolean updating;
result.metadata = osgiManager.getManagedBundle(inferredMetadata.getVersionedName());
if (result.getMetadata()!=null) {
+
// already have a managed bundle - check if this is using a new/different URL
if (suppliedKnownBundleMetadata!=null && suppliedKnownBundleMetadata.getUrl()!=null) {
String knownIdForThisUrl = osgiManager.managedBundlesRecord.getManagedBundleIdFromUrl(suppliedKnownBundleMetadata.getUrl());
@@ -376,68 +378,106 @@ class OsgiArchiveInstaller {
osgiManager.managedBundlesRecord.setManagedBundleUrl(suppliedKnownBundleMetadata.getUrl(), result.getMetadata().getId());
}
}
+
+ result.bundle = osgiManager.framework.getBundleContext().getBundle(result.getMetadata().getOsgiUniqueUrl());
+
+ // Check if exactly this bundle is already installed
+ if (result.bundle != null && checksumsMatch(result.getMetadata(), inferredMetadata)) {
+ // e.g. repeatedly installing the same bundle
+ log.trace("Bundle "+inferredMetadata+" matches already installed managed bundle "+result.getMetadata()
+ +"; install is no-op");
+ result.setIgnoringAlreadyInstalled();
+ return ReferenceWithError.newInstanceWithoutError(result);
+ } else if (isEquivalentBundleAlreadyOsgiInstalled(osgiManager, inferredMetadata, zipFile)) {
+ // e.g. happens if pre-installed bundle is brought under management, and then add it again via a mvn-style url.
+ // We wouldn't know the checksum from the pre-installed bundle.
+ log.trace("Bundle "+inferredMetadata+" matches metadata of managed bundle "+result.getMetadata()
+ +" (but not OSGi bundle location "+result.getMetadata().getOsgiUniqueUrl()+"), "
+ + "and matches already installed OSGi bundle; ; install is no-op");
+ result.setIgnoringAlreadyInstalled();
+ return ReferenceWithError.newInstanceWithoutError(result);
+ }
+
if (canUpdate()) {
-
- result.bundle = osgiManager.framework.getBundleContext().getBundle(result.getMetadata().getOsgiUniqueUrl());
- if (result.getBundle()==null) {
- if (hasBundleOnInstall(osgiManager, inferredMetadata, zipFile)) {
- // e.g. happens if system-bundle is brought under management, and then try to add it again:
- // if added from "initial catalog" has it, and then added from persisted state as well.
- result.setIgnoringAlreadyInstalled();
- return ReferenceWithError.newInstanceWithoutError(result);
- } else {
- log.warn("Brooklyn thought is was already managing bundle "+result.getMetadata().getVersionedName()+" but it's not installed to framework; reinstalling it");
- updating = false;
- }
+ if (result.getBundle() == null) {
+ log.warn("Brooklyn thought is was already managing bundle "+result.getMetadata().getVersionedName()
+ +" but it's not installed to framework at location "+result.getMetadata().getOsgiUniqueUrl()+"; reinstalling it");
+ updating = false;
} else {
+ log.trace("Updating existing brooklyn-managed bundle "+result);
updating = true;
}
} else {
- if (result.getMetadata().getChecksum()==null || inferredMetadata.getChecksum()==null) {
- log.warn("Missing bundle checksum data for "+result+"; assuming bundle replacement is permitted");
- } else if (!Objects.equal(result.getMetadata().getChecksum(), inferredMetadata.getChecksum())) {
+ List<Bundle> existingBundles = findBundlesByVersion(osgiManager, inferredMetadata);
+ if (existingBundles.size() > 0 && (result.getMetadata().getChecksum()==null || inferredMetadata.getChecksum()==null)) {
+ // e.g. Checksum would be missing if we brought under management a pre-installed bundle with an unusable url.
+ log.info("Missing bundle checksum data for "+result+"; assuming bundle matches existing brooklyn-managed bundle (not re-installing)");
+ result.setIgnoringAlreadyInstalled();
+ return ReferenceWithError.newInstanceWithoutError(result);
+ } else if (result.bundle != null || existingBundles.size() > 0) {
throw new IllegalArgumentException("Bundle "+result.getMetadata().getVersionedName()+" already installed; "
- + "cannot install a different bundle at a same non-snapshot version");
+ + "cannot install a different bundle with the same non-snapshot version");
+ } else {
+ throw new IllegalArgumentException("Bundle "+result.getMetadata().getVersionedName()+" already a brooklyn-managed bundle, but not found in OSGi framework; "
+ + "will not re-install without use of 'force'");
}
- result.setIgnoringAlreadyInstalled();
- return ReferenceWithError.newInstanceWithoutError(result);
}
} else {
+ // No such Brooklyn-managed bundle.
+
+ // Check if likely-looking bundle already installed to OSGi subsystem, but brooklyn not aware of it.
+ // This will often happen on a karaf restart where bundle was cached by karaf, so we need to allow it;
+ // can also happen if brooklyn.libraries references an existing bundle.
+ //
+ // If we're not certain that the bundle is identical
+
+
result.metadata = inferredMetadata;
- // no such Brooklyn-managed bundle
- Maybe<Bundle> b = Osgis.bundleFinder(osgiManager.framework).symbolicName(result.getMetadata().getSymbolicName()).version(result.getMetadata().getSuppliedVersionString()).find();
- if (b.isPresent()) {
- // bundle already installed to OSGi subsystem but brooklyn not aware of it;
- // this will often happen on a karaf restart where bundle was cached by karaf,
- // so we need to allow it. can also happen if brooklyn.libraries references an existing bundle.
+
+ // search for already-installed bundles.
+ List<Bundle> existingBundles = findBundlesByVersion(osgiManager, inferredMetadata);
+ Maybe<Bundle> existingEquivalentBundle = tryFindEquivalentBundle(existingBundles, inferredMetadata, zipFile);
+
+ if (existingEquivalentBundle.isPresent()) {
+ // Identical bundle (by osgi location or binary content) already installed; just bring that under management.
+ // This will often happen on a karaf restart: bundles from persisted state match those cached by karaf,
+ isBringingExistingOsgiInstalledBundleUnderBrooklynManagement = true;
+ result.bundle = existingEquivalentBundle.get();
- // determine if we are simply bringing existing installed under Brooklyn management (because url or binary content identical and not forced)
- // or if the bundle should be reinstalled/updated
- if (!force) {
- if (!isBringingExistingOsgiInstalledBundleUnderBrooklynManagement) {
- isBringingExistingOsgiInstalledBundleUnderBrooklynManagement = hasBundleOnInstall(osgiManager, inferredMetadata, zipFile);
- }
- } else {
+ } else if (existingBundles.size() > 0) {
+ Bundle existingBundle = existingBundles.get(0);
+
+ if (force) {
if (isBringingExistingOsgiInstalledBundleUnderBrooklynManagement) {
log.debug("Request to install "+inferredMetadata+" was forced, so forcing reinstallation "
- + "of existing OSGi installed (but not Brooklyn-managed) bundle "+b.get());
+ + "of existing OSGi installed (but not Brooklyn-managed) bundle "+existingBundle);
isBringingExistingOsgiInstalledBundleUnderBrooklynManagement = false;
}
}
if (isBringingExistingOsgiInstalledBundleUnderBrooklynManagement) {
- result.bundle = b.get();
-
+ // We were explicitly asked to bring an existing OSGi bundle under management;
+ // no equivalence check required
+ result.bundle = existingBundle;
} else {
- // needs reinstallation
- // we could update instead of uninstall/reinstall
- // note however either way we won't be able to rollback if there is a failure
+ // Uninstall and re-install the bundle.
+ // This is a good idea for brooklyn managed bundles that were in the karaf cache (when we can't
+ // determine that they are definitely identical).
+ // It's less good for pre-installed bundles, but if the user has said to deploy it or has
+ // referenced it in `brooklyn.libraries` then we'll go for it anyway! Let's hope they didn't
+ // reference `org.apache.brooklyn.core` or some such.
+ //
+ // We are this extreme because we want rebind to always work! If a user did a `force` install
+ // of a bundle, then we want to do the same on rebind (rather than risk failing).
+ //
+ // Instead of uninstall, we could update the bundle.
+ // Note however either way we won't be able to rollback if there is a failure
log.debug("Brooklyn install of "+result.getMetadata().getVersionedName()+" detected already loaded in OSGi; uninstalling that to reinstall as Brooklyn-managed");
- b.get().uninstall();
+ existingBundle.uninstall();
result.bundle = null;
}
}
- // normal install
+
updating = false;
}
@@ -449,6 +489,7 @@ class OsgiArchiveInstaller {
log.debug("Brooklyn install of "+result.getMetadata().getVersionedName()+" detected already loaded "+result.getBundle()+" in OSGi can be re-used, skipping OSGi install");
} else {
assert result.getBundle()==null;
+ log.debug("Installing bundle "+result.getMetadata().getVersionedName()+", using OSGi location "+result.getMetadata().getOsgiUniqueUrl());
result.bundle = osgiManager.framework.getBundleContext().installBundle(result.getMetadata().getOsgiUniqueUrl(), fin);
}
} else {
@@ -464,14 +505,18 @@ class OsgiArchiveInstaller {
osgiManager.managedBundlesRecord.addManagedBundle(result, zipFile);
result.code = OsgiBundleInstallationResult.ResultCode.INSTALLED_NEW_BUNDLE;
result.message = "Installed Brooklyn catalog bundle "+result.getMetadata().getVersionedName()+" with ID "+result.getMetadata().getId()+" ["+result.bundle.getBundleId()+"]";
- ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
- mgmt().getRebindManager().getChangeListener().onManaged(result.getMetadata());
+ if (!isBlacklistedForPersistence(result.getMetadata())) {
+ ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
+ mgmt().getRebindManager().getChangeListener().onManaged(result.getMetadata());
+ }
} else {
oldZipFile = osgiManager.managedBundlesRecord.updateManagedBundleFile(result, zipFile);
result.code = OsgiBundleInstallationResult.ResultCode.UPDATED_EXISTING_BUNDLE;
result.message = "Updated Brooklyn catalog bundle "+result.getMetadata().getVersionedName()+" as existing ID "+result.getMetadata().getId()+" ["+result.bundle.getBundleId()+"]";
- ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
- mgmt().getRebindManager().getChangeListener().onChanged(result.getMetadata());
+ if (!isBlacklistedForPersistence(result.getMetadata())) {
+ ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
+ mgmt().getRebindManager().getChangeListener().onChanged(result.getMetadata());
+ }
}
log.debug(result.message + " (partial): OSGi bundle installed, with bundle start and Brooklyn management to follow");
// can now delete and close (copy has been made and is available from OsgiManager)
@@ -505,8 +550,10 @@ class OsgiArchiveInstaller {
+ "installation will likely be corrupted and correct version should be manually installed.", e);
}
- ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
- mgmt().getRebindManager().getChangeListener().onChanged(result.getMetadata());
+ if (!isBlacklistedForPersistence(result.getMetadata())) {
+ ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
+ mgmt().getRebindManager().getChangeListener().onChanged(result.getMetadata());
+ }
} else {
if (isBringingExistingOsgiInstalledBundleUnderBrooklynManagement) {
log.debug("Uninstalling bundle "+result.getVersionedName()+" from Brooklyn management only (rollback needed but it was already installed to OSGi)");
@@ -514,8 +561,10 @@ class OsgiArchiveInstaller {
log.debug("Uninstalling bundle "+result.getVersionedName()+" (roll back of failed fresh install, no previous version to revert to)");
}
osgiManager.uninstallUploadedBundle(result.getMetadata(), false, isBringingExistingOsgiInstalledBundleUnderBrooklynManagement);
- ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
- mgmt().getRebindManager().getChangeListener().onUnmanaged(result.getMetadata());
+ if (!isBlacklistedForPersistence(result.getMetadata())) {
+ ((BasicManagedBundle)result.getMetadata()).setPersistenceNeeded(true);
+ mgmt().getRebindManager().getChangeListener().onUnmanaged(result.getMetadata());
+ }
}
}
public void run() {
@@ -631,47 +680,75 @@ class OsgiArchiveInstaller {
close();
}
}
-
- private static boolean hasBundleOnInstall(OsgiManager osgiManager, ManagedBundle desired, File zipFile) {
+
+ private static boolean isBlacklistedForPersistence(ManagedBundle managedBundle) {
+ // Specifically, we treat as "managed bundles" (to extract their catalog.bom) the contents of:
+ // - org.apache.brooklyn.core
+ // - org.apache.brooklyn.policy
+ // - org.apache.brooklyn.test-framework
+ // - org.apache.brooklyn.software-*
+ // - org.apache.brooklyn.library-catalog
+ // - org.apache.brooklyn.karaf-init (not sure why this one could end up in persisted state!)
+ return managedBundle.getSymbolicName().startsWith("org.apache.brooklyn.");
+ }
+
+ private static List<Bundle> findBundlesByVersion(OsgiManager osgiManager, ManagedBundle desired) {
+ return Osgis.bundleFinder(osgiManager.framework).symbolicName(desired.getSymbolicName()).version(desired.getOsgiVersionString()).findAll();
+ }
+
+ private static boolean checksumsMatch(ManagedBundle actual, ManagedBundle desired) {
+ return actual.getChecksum() != null && Objects.equal(actual.getChecksum(), desired.getChecksum());
+ }
+
+ private static boolean isEquivalentBundleAlreadyOsgiInstalled(OsgiManager osgiManager, ManagedBundle desired, File zipFile) {
+ for (Bundle bundle : findBundlesByVersion(osgiManager, desired)) {
+ if (isEquivalentBundle(bundle, desired, zipFile)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static Maybe<Bundle> tryFindEquivalentBundle(Iterable<? extends Bundle> bundles, ManagedBundle desired, File zipFile) {
+ for (Bundle bundle : bundles) {
+ if (isEquivalentBundle(bundle, desired, zipFile)) {
+ return Maybe.of(bundle);
+ }
+ }
+
+ return Maybe.absent();
+ }
+
+ private static boolean isEquivalentBundle(Bundle bundle, ManagedBundle desired, File zipFile) {
// Would be nice to also use `desired.getChecksum()`, but not clear if we can get
// MD5 checksum from an installed OSGi bundle.
- Maybe<Bundle> b = Osgis.bundleFinder(osgiManager.framework).symbolicName(desired.getSymbolicName()).version(desired.getOsgiVersionString()).find();
- if (b.isPresent()) {
- // bundle already installed to OSGi subsystem (but brooklyn not aware of it as "managed bundle");
- // this will often happen on a karaf restart where bundle was cached by karaf,
- // so we need to allow it. can also happen if brooklyn.libraries references an existing bundle.
-
- // determine if we are simply bringing existing installed under Brooklyn management (because url or binary content identical and not forced)
- // or if the bundle should be reinstalled/updated
- if (Objects.equal(b.get().getLocation(), desired.getUrl())) {
- // installation request was for identical location, so assume we are simply bringing under mgmt
- log.debug("Request to install "+desired+" from same location "+b.get().getLocation()+
- " as existing OSGi installed (but not Brooklyn-managed) bundle "+b.get()+", so skipping reinstall");
- return true;
- } else {
- // different locations, but see if we can compare input stream contents
- // (prevents needless uninstall/reinstall of already installed bundles)
- try {
- if (Streams.compare(new FileInputStream(zipFile), new URL(b.get().getLocation()).openStream())) {
- log.debug("Request to install "+desired+" has same contents"+
- " as existing OSGi installed (but not Brooklyn-managed) bundle "+b.get()+", so skipping reinstall");
- return true;
- } else {
- log.debug("Request to install "+desired+" has different contents"+
- " as existing OSGi installed (but not Brooklyn-managed) bundle "+b.get()+", so will do reinstall");
- return false;
- }
- } catch (Exception e) {
- Exceptions.propagateIfFatal(e);
- // probably an invalid URL on installed bundle; that's allowed
- log.debug("Request to install "+desired+" could not compare contents"+
- " with existing OSGi installed (but not Brooklyn-managed) bundle "+b.get()+", so will do reinstall (error "+e+" loading from "+b.get().getLocation()+")");
+ if (Objects.equal(bundle.getLocation(), desired.getUrl())) {
+ // installation request was for identical location, so assume we are simply bringing under mgmt
+ log.debug("Request to install "+desired+" from same location "+bundle.getLocation()+
+ " as existing OSGi installed (but not Brooklyn-managed) bundle "+bundle+", so skipping reinstall");
+ return true;
+ } else {
+ // different locations, but see if we can compare input stream contents
+ // (prevents needless uninstall/reinstall of already installed bundles)
+ try {
+ if (Streams.compare(new FileInputStream(zipFile), new URL(bundle.getLocation()).openStream())) {
+ log.debug("Request to install "+desired+" has same contents"+
+ " as existing OSGi installed (but not Brooklyn-managed) bundle "+bundle+", so skipping reinstall");
+ return true;
+ } else {
+ log.debug("Request to install "+desired+" has different contents"+
+ " as existing OSGi installed (but not Brooklyn-managed) bundle "+bundle+", so will do reinstall (if no other equivalents found)");
return false;
}
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ // probably an invalid URL on installed bundle; that's allowed
+ log.debug("Request to install "+desired+" could not compare contents"+
+ " with existing OSGi installed (but not Brooklyn-managed) bundle "+bundle+", so will do reinstall if not other equivalents found (error "+e+" loading from "+bundle.getLocation()+")");
+ return false;
}
- } else {
- return false;
}
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
index 755107a..8a667eb 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
@@ -1233,7 +1233,7 @@ public abstract class RebindIteration {
}
protected ManagedBundle newManagedBundle(ManagedBundleMemento memento) {
- ManagedBundle result = new BasicManagedBundle(memento.getSymbolicName(), memento.getVersion(), memento.getUrl());
+ ManagedBundle result = new BasicManagedBundle(memento.getSymbolicName(), memento.getVersion(), memento.getUrl(), memento.getChecksum());
FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", memento.getId()), result);
return result;
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicManagedBundleMemento.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicManagedBundleMemento.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicManagedBundleMemento.java
index 505e7c8..37afb58 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicManagedBundleMemento.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicManagedBundleMemento.java
@@ -42,7 +42,8 @@ public class BasicManagedBundleMemento extends AbstractMemento implements Manage
protected String symbolicName;
protected String version;
protected String url;
-
+ protected String checksum;
+
public Builder symbolicName(String symbolicName) {
this.symbolicName = symbolicName;
return self();
@@ -58,11 +59,17 @@ public class BasicManagedBundleMemento extends AbstractMemento implements Manage
return self();
}
+ public Builder checksum(String checksum) {
+ this.checksum = checksum;
+ return self();
+ }
+
public Builder from(ManagedBundleMemento other) {
super.from(other);
symbolicName = other.getSymbolicName();
version = other.getVersion();
url = other.getUrl();
+ checksum = other.getChecksum();
return self();
}
@@ -74,6 +81,7 @@ public class BasicManagedBundleMemento extends AbstractMemento implements Manage
private String symbolicName;
private String version;
private String url;
+ private String checksum;
transient private ByteSource jarContent;
@SuppressWarnings("unused") // For deserialisation
@@ -84,6 +92,7 @@ public class BasicManagedBundleMemento extends AbstractMemento implements Manage
this.symbolicName = builder.symbolicName;
this.version = builder.version;
this.url = builder.url;
+ this.checksum = builder.checksum;
}
@Override
@@ -102,6 +111,11 @@ public class BasicManagedBundleMemento extends AbstractMemento implements Manage
}
@Override
+ public String getChecksum() {
+ return checksum;
+ }
+
+ @Override
public ByteSource getJarContent() {
return jarContent;
}
@@ -129,7 +143,7 @@ public class BasicManagedBundleMemento extends AbstractMemento implements Manage
return super.newVerboseStringHelper()
.add("symbolicName", getSymbolicName())
.add("version", getVersion())
- .add("url", getUrl());
+ .add("url", getUrl())
+ .add("checksum", getChecksum());
}
-
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
index 8372b99..860bf5a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
@@ -20,6 +20,8 @@ package org.apache.brooklyn.core.typereg;
import java.util.Map;
+import javax.annotation.Nullable;
+
import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
import org.apache.brooklyn.api.typereg.ManagedBundle;
@@ -46,7 +48,7 @@ public class BasicManagedBundle extends AbstractBrooklynObject implements Manage
/** Creates an empty one, with an ID, expecting other fields will be populated. */
public BasicManagedBundle() {}
- public BasicManagedBundle(String name, String version, String url) {
+ public BasicManagedBundle(String name, String version, String url, @Nullable String checksum) {
if (name == null && version == null) {
Preconditions.checkNotNull(url, "Either a URL or both name and version are required");
} else {
@@ -56,6 +58,7 @@ public class BasicManagedBundle extends AbstractBrooklynObject implements Manage
this.symbolicName = name;
this.version = version;
this.url = url;
+ this.checksum = checksum;
}
@Override
@@ -101,9 +104,16 @@ public class BasicManagedBundle extends AbstractBrooklynObject implements Manage
this.url = url;
}
+ /**
+ * Gets the (internal) value to be used as the location in bundleContext.install(location).
+ * It thus allows us to tell if a cached OSGi bundle is the same as a bundle we are about to
+ * install (e.g. one we get from persisted state), or have retrieved from the initial catalog.
+ *
+ * Care should be taken to set the checksum <em>before</em> using the OSGi unique url.
+ */
@Override
public String getOsgiUniqueUrl() {
- return "brooklyn:"+getId();
+ return "brooklyn:" + (checksum != null ? checksum : getId());
}
@Override
@@ -175,6 +185,7 @@ public class BasicManagedBundle extends AbstractBrooklynObject implements Manage
public String getChecksum() {
return checksum;
}
+
public void setChecksum(String md5Checksum) {
this.checksum = md5Checksum;
}
@@ -184,8 +195,9 @@ public class BasicManagedBundle extends AbstractBrooklynObject implements Manage
throw new UnsupportedOperationException();
}
- public static ManagedBundle of(CatalogBundle bundleUrl) {
- return new BasicManagedBundle(bundleUrl.getSymbolicName(), bundleUrl.getSuppliedVersionString(), bundleUrl.getUrl());
+ public static ManagedBundle of(CatalogBundle bundle) {
+ String checksum = (bundle instanceof ManagedBundle) ? ((ManagedBundle)bundle).getChecksum() : null;
+ return new BasicManagedBundle(bundle.getSymbolicName(), bundle.getSuppliedVersionString(), bundle.getUrl(), checksum);
}
public void setPersistenceNeeded(boolean val) {
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/launcher/src/test/java/org/apache/brooklyn/launcher/AbstractBrooklynLauncherRebindTest.java
----------------------------------------------------------------------
diff --git a/launcher/src/test/java/org/apache/brooklyn/launcher/AbstractBrooklynLauncherRebindTest.java b/launcher/src/test/java/org/apache/brooklyn/launcher/AbstractBrooklynLauncherRebindTest.java
index 89bcae0..400e3a8 100644
--- a/launcher/src/test/java/org/apache/brooklyn/launcher/AbstractBrooklynLauncherRebindTest.java
+++ b/launcher/src/test/java/org/apache/brooklyn/launcher/AbstractBrooklynLauncherRebindTest.java
@@ -267,19 +267,32 @@ public abstract class AbstractBrooklynLauncherRebindTest {
}
protected String createCatalogYaml(Iterable<URI> libraries, Iterable<VersionedName> entities) {
- if (Iterables.isEmpty(libraries) && Iterables.isEmpty(entities)) {
- return "brooklyn.catalog: {}\n";
- }
-
+ return createCatalogYaml(libraries, ImmutableList.of(), entities, null);
+ }
+
+ protected String createCatalogYaml(Iterable<URI> libraryUris, Iterable<VersionedName> libraryNames, Iterable<VersionedName> entities) {
+ return createCatalogYaml(libraryUris, libraryNames, entities, null);
+ }
+
+ protected String createCatalogYaml(Iterable<URI> libraryUris, Iterable<VersionedName> libraryNames, Iterable<VersionedName> entities, String randomNoise) {
StringBuilder result = new StringBuilder();
result.append("brooklyn.catalog:\n");
- if (!Iterables.isEmpty(libraries)) {
+ if (randomNoise != null) {
+ result.append(" description: "+randomNoise+"\n");
+ }
+ if (!(Iterables.isEmpty(libraryUris) && Iterables.isEmpty(libraryNames))) {
result.append(" brooklyn.libraries:\n");
}
- for (URI library : libraries) {
+ for (URI library : libraryUris) {
result.append(" - " + library+"\n");
}
- if (!Iterables.isEmpty(entities)) {
+ for (VersionedName library : libraryNames) {
+ result.append(" - name: "+library.getSymbolicName()+"\n");
+ result.append(" version: \""+library.getVersionString()+"\"\n");
+ }
+ if (Iterables.isEmpty(entities)) {
+ result.append(" items: []\n");
+ } else {
result.append(" items:\n");
}
for (VersionedName entity : entities) {
@@ -292,6 +305,9 @@ public abstract class AbstractBrooklynLauncherRebindTest {
return result.toString();
}
+
+
+
public PersistedStateInitializer newPersistedStateInitializer() {
return new PersistedStateInitializer();
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/85196899/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogOsgiTest.java
----------------------------------------------------------------------
diff --git a/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogOsgiTest.java b/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogOsgiTest.java
index d4e8c75..8985147 100644
--- a/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogOsgiTest.java
+++ b/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogOsgiTest.java
@@ -19,30 +19,31 @@
package org.apache.brooklyn.launcher;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
import java.io.File;
-import java.nio.charset.StandardCharsets;
import java.io.FileInputStream;
-import java.net.URI;
-import java.util.LinkedHashSet;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
-import org.apache.brooklyn.api.typereg.ManagedBundle;
-import org.apache.brooklyn.api.typereg.RegisteredType;
import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
+import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
-import org.apache.brooklyn.entity.stock.BasicEntity;
import org.apache.brooklyn.test.support.TestResourceUnavailableException;
import org.apache.brooklyn.util.core.osgi.Osgis;
+import org.apache.brooklyn.util.exceptions.ReferenceWithError;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.osgi.OsgiTestResources;
import org.apache.brooklyn.util.osgi.VersionedName;
+import org.apache.brooklyn.util.text.Identifiers;
import org.osgi.framework.Bundle;
import org.osgi.framework.launch.Framework;
-import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import com.google.common.base.Joiner;
@@ -62,12 +63,29 @@ public class BrooklynLauncherRebindCatalogOsgiTest extends AbstractBrooklynLaunc
private static final Set<VersionedName> COM_EXAMPLE_BUNDLE_CATALOG_IDS = ImmutableSet.of(
new VersionedName("com.example.simpleTest", "0.0.0-SNAPSHOT"));
+ /**
+ * Whether we reuse OSGi Framework depends if we want it to feel like rebinding on a new machine
+ * (so no cached bundles), or a local restart. We can also use {@code reuseOsgi = true} to emulate
+ * system bundles (by pre-installing them into the reused framework at the start of the test).
+ */
private boolean reuseOsgi = false;
- @BeforeMethod(alwaysRun=true)
+ private List<Bundle> reusedBundles = new ArrayList<>();
+
+ @AfterMethod(alwaysRun=true)
@Override
- public void setUp() throws Exception {
- super.setUp();
+ public void tearDown() throws Exception {
+ try {
+ reuseOsgi = false;
+ for (Bundle bundle : reusedBundles) {
+ if (bundle != null && bundle.getState() != Bundle.UNINSTALLED) {
+ bundle.uninstall();
+ }
+ }
+ reusedBundles.clear();
+ } finally {
+ super.tearDown();
+ }
}
@Override
@@ -110,7 +128,7 @@ public class BrooklynLauncherRebindCatalogOsgiTest extends AbstractBrooklynLaunc
/**
* See https://issues.apache.org/jira/browse/BROOKLYN-546.
*
- * We built up to launcher3, which will have three things:
+ * We built up to launcher2, which will have three things:
* 1. a pre-installed "system bundle"
* 2. an initial catalog that references this same system bundle (hence the bundle will be "managed")
* 3. persisted state that references this same system bundle.
@@ -130,54 +148,201 @@ public class BrooklynLauncherRebindCatalogOsgiTest extends AbstractBrooklynLaunc
@Test
public void testRebindWithSystemBundleInCatalog() throws Exception {
Set<VersionedName> bundleItems = ImmutableSet.of(VersionedName.fromString("one:1.0.0-SNAPSHOT"));
- String bundleBom = createCatalogYaml(ImmutableList.of(), bundleItems);
- VersionedName bundleName = new VersionedName("org.example.testRebindWithSystemBundleInCatalog", "1.0.0.SNAPSHOT");
- File bundleFile = newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, bundleBom.getBytes(StandardCharsets.UTF_8)), bundleName);
+ VersionedName bundleName = new VersionedName("org.example.brooklynLauncherRebindCatalogOsgiTest."+Identifiers.makeRandomId(4), "1.0.0.SNAPSHOT");
+ File bundleFile = newTmpBundle(bundleItems, bundleName);
File bundleFileCopy = newTmpCopy(bundleFile);
File initialBomFile = newTmpFile(createCatalogYaml(ImmutableList.of(bundleFile.toURI()), ImmutableList.of()));
- Framework reusedFamework = null;
- Bundle pseudoSystemBundle = null;
- try {
- reuseOsgi = true;
-
- // Launch and terminate brooklyn to ensure there is a reusable OSGi Framework
- BrooklynLauncher launcher = newLauncherForTests(CATALOG_EMPTY_INITIAL);
- launcher.start();
- launcher.terminate();
- Os.deleteRecursively(persistenceDir);
-
- // Add our bundle, so it feels for all intents and purposes like a "system bundle"
- reusedFamework = OsgiManager.tryPeekFrameworkForReuse().get();
- pseudoSystemBundle = installBundle(reusedFamework, bundleFileCopy);
-
- // Launch brooklyn, where initial catalog includes a duplicate of the system bundle
- BrooklynLauncher launcher2 = newLauncherForTests(initialBomFile.getAbsolutePath());
- launcher2.start();
- assertCatalogConsistsOfIds(launcher2, bundleItems);
- assertManagedBundle(launcher2, bundleName, bundleItems);
- launcher2.terminate();
-
- // Launch brooklyn, where persisted state now includes the initial catalog's bundle
- BrooklynLauncher launcher3 = newLauncherForTests(initialBomFile.getAbsolutePath());
- launcher3.start();
- assertCatalogConsistsOfIds(launcher3, bundleItems);
- assertManagedBundle(launcher3, bundleName, bundleItems);
-
- // Should not have replaced the original "system bundle"
- reusedFamework.getBundleContext().getBundles();
- List<Bundle> matchingBundles = Osgis.bundleFinder(reusedFamework).symbolicName(bundleName.getSymbolicName()).version(bundleName.getOsgiVersionString()).findAll();
- assertEquals(matchingBundles, ImmutableList.of(pseudoSystemBundle));
-
- launcher3.terminate();
+ reuseOsgi = true;
+
+ // Add our bundle, so it feels for all intents and purposes like a "system bundle"
+ Framework reusedFramework = initReusableOsgiFramework();
+ Bundle pseudoSystemBundle = installBundle(reusedFramework, bundleFileCopy);
+ reusedBundles.add(pseudoSystemBundle);
+
+ // Launch brooklyn, where initial catalog includes a duplicate of the system bundle
+ BrooklynLauncher launcher = newLauncherForTests(initialBomFile.getAbsolutePath());
+ launcher.start();
+ assertCatalogConsistsOfIds(launcher, bundleItems);
+ assertManagedBundle(launcher, bundleName, bundleItems);
+ launcher.terminate();
- } finally {
- reuseOsgi = false;
- if (pseudoSystemBundle != null && pseudoSystemBundle.getState() != Bundle.UNINSTALLED) {
- pseudoSystemBundle.uninstall();
- }
+ // Launch brooklyn, where persisted state now includes the initial catalog's bundle
+ BrooklynLauncher launcher2 = newLauncherForTests(initialBomFile.getAbsolutePath());
+ launcher2.start();
+ assertCatalogConsistsOfIds(launcher2, bundleItems);
+ assertManagedBundle(launcher2, bundleName, bundleItems);
+
+ // Should not have replaced the original "system bundle"
+ assertOnlyBundle(reusedFramework, bundleName, pseudoSystemBundle);
+
+ launcher2.terminate();
+ }
+
+ @Test
+ public void testInstallPreexistingBundle() throws Exception {
+ Set<VersionedName> bundleItems = ImmutableSet.of(VersionedName.fromString("one:1.0.0-SNAPSHOT"));
+ VersionedName bundleName = new VersionedName("org.example.brooklynLauncherRebindCatalogOsgiTest."+Identifiers.makeRandomId(4), "1.0.0.SNAPSHOT");
+ File bundleFile = newTmpBundle(bundleItems, bundleName);
+ File bundleFileCopy = newTmpCopy(bundleFile);
+
+ reuseOsgi = true;
+
+ // Add our bundle, so it feels for all intents and purposes like a "system bundle"
+ Framework reusedFramework = initReusableOsgiFramework();
+ Bundle pseudoSystemBundle = installBundle(reusedFramework, bundleFileCopy);
+ reusedBundles.add(pseudoSystemBundle);
+
+ // Launch brooklyn, and explicitly install pre-existing bundle.
+ // Should bring it under brooklyn-management (should not re-install it).
+ BrooklynLauncher launcher = newLauncherForTests(CATALOG_EMPTY_INITIAL);
+ launcher.start();
+ installBrooklynBundle(launcher, bundleFile, false).getWithError();
+
+ assertOnlyBundle(launcher, bundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher, bundleItems);
+ assertManagedBundle(launcher, bundleName, bundleItems);
+ launcher.terminate();
+
+ // Launch brooklyn again (because will have persisted the pre-installed bundle)
+ BrooklynLauncher launcher2 = newLauncherForTests(CATALOG_EMPTY_INITIAL);
+ launcher2.start();
+ assertOnlyBundle(reusedFramework, bundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher2, bundleItems);
+ assertManagedBundle(launcher2, bundleName, bundleItems);
+ launcher2.terminate();
+ }
+
+ @Test
+ public void testInstallPreexistingBundleViaIndirectBrooklynLibrariesReference() throws Exception {
+ Set<VersionedName> bundleItems = ImmutableSet.of(VersionedName.fromString("one:1.0.0-SNAPSHOT"));
+ VersionedName systemBundleName = new VersionedName("org.example.brooklynLauncherRebindCatalogOsgiTest.system"+Identifiers.makeRandomId(4), "1.0.0.SNAPSHOT");
+ File systemBundleFile = newTmpBundle(bundleItems, systemBundleName);
+
+ String bundleBom = createCatalogYaml(ImmutableList.of(), ImmutableList.of(systemBundleName), ImmutableList.of());
+ VersionedName bundleName = new VersionedName("org.example.brooklynLauncherRebindCatalogOsgiTest.initial"+Identifiers.makeRandomId(4), "1.0.0.SNAPSHOT");
+ File bundleFile = newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, bundleBom.getBytes(StandardCharsets.UTF_8)), bundleName);
+
+ File initialBomFile = newTmpFile(createCatalogYaml(ImmutableList.of(bundleFile.toURI()), ImmutableList.of()));
+
+ reuseOsgi = true;
+
+ // Add our bundle, so it feels for all intents and purposes like a "system bundle"
+ Framework reusedFramework = initReusableOsgiFramework();
+ Bundle pseudoSystemBundle = installBundle(reusedFramework, systemBundleFile);
+ reusedBundles.add(pseudoSystemBundle);
+
+ // Launch brooklyn, with initial catalog pointing at bundle that points at system bundle.
+ // Should bring it under brooklyn-management (without re-installing it).
+ BrooklynLauncher launcher = newLauncherForTests(initialBomFile.getAbsolutePath());
+ launcher.start();
+ assertOnlyBundle(launcher, systemBundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher, bundleItems);
+ assertManagedBundle(launcher, systemBundleName, bundleItems);
+ assertManagedBundle(launcher, bundleName, ImmutableSet.of());
+ launcher.terminate();
+
+ // Launch brooklyn again (because will have persisted both those bundles)
+ BrooklynLauncher launcher2 = newLauncherForTests(CATALOG_EMPTY_INITIAL);
+ launcher2.start();
+ assertOnlyBundle(launcher2, systemBundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher2, bundleItems);
+ assertManagedBundle(launcher2, systemBundleName, bundleItems);
+ assertManagedBundle(launcher2, bundleName, ImmutableSet.of());
+ launcher2.terminate();
+ }
+
+ @Test
+ public void testInstallPreexistingBundleViaInitialBomBrooklynLibrariesReference() throws Exception {
+ runInstallPreexistingBundleViaInitialBomBrooklynLibrariesReference(false);
+ }
+
+ // Aled thought we supported version ranges in 'brooklyn.libraries', but doesn't work here.
+ @Test(groups="Broken")
+ public void testInstallPreexistingBundleViaInitialBomBrooklynLibrariesReferenceWithVersionRange() throws Exception {
+ runInstallPreexistingBundleViaInitialBomBrooklynLibrariesReference(true);
+ }
+
+ protected void runInstallPreexistingBundleViaInitialBomBrooklynLibrariesReference(boolean useVersionRange) throws Exception {
+ Set<VersionedName> bundleItems = ImmutableSet.of(VersionedName.fromString("one:1.0.0"));
+ VersionedName systemBundleName = new VersionedName("org.example.brooklynLauncherRebindCatalogOsgiTest.system"+Identifiers.makeRandomId(4), "1.0.0");
+ File systemBundleFile = newTmpBundle(bundleItems, systemBundleName);
+
+ VersionedName systemBundleNameRef;
+ if (useVersionRange) {
+ systemBundleNameRef = new VersionedName(systemBundleName.getSymbolicName(), "[1,2)");
+ } else {
+ systemBundleNameRef = systemBundleName;
}
+ File initialBomFile = newTmpFile(createCatalogYaml(ImmutableList.of(), ImmutableList.of(systemBundleNameRef), ImmutableList.of()));
+
+ reuseOsgi = true;
+
+ // Add our bundle, so it feels for all intents and purposes like a "system bundle"
+ Framework reusedFramework = initReusableOsgiFramework();
+ Bundle pseudoSystemBundle = installBundle(reusedFramework, systemBundleFile);
+ reusedBundles.add(pseudoSystemBundle);
+
+ // Launch brooklyn, with initial catalog pointing at system bundle.
+ // Should bring it under brooklyn-management (without re-installing it).
+ BrooklynLauncher launcher = newLauncherForTests(initialBomFile.getAbsolutePath());
+ launcher.start();
+ assertOnlyBundle(launcher, systemBundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher, bundleItems);
+ assertManagedBundle(launcher, systemBundleName, bundleItems);
+ launcher.terminate();
+
+ // Launch brooklyn again (because will have persisted both those bundles)
+ BrooklynLauncher launcher2 = newLauncherForTests(CATALOG_EMPTY_INITIAL);
+ launcher2.start();
+ assertOnlyBundle(launcher2, systemBundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher2, bundleItems);
+ assertManagedBundle(launcher2, systemBundleName, bundleItems);
+ launcher2.terminate();
+ }
+
+ @Test
+ public void testInstallReplacesPreexistingBundleWithoutForce() throws Exception {
+ runInstallReplacesPreexistingBundle(false);
+ }
+
+ @Test
+ public void testInstallReplacesPreexistingBundleWithForce() throws Exception {
+ runInstallReplacesPreexistingBundle(true);
+ }
+
+ protected void runInstallReplacesPreexistingBundle(boolean force) throws Exception {
+ Set<VersionedName> bundleItems = ImmutableSet.of(VersionedName.fromString("one:1.0.0-SNAPSHOT"));
+ VersionedName bundleName = new VersionedName("org.example.brooklynLauncherRebindCatalogOsgiTest."+Identifiers.makeRandomId(4), "1.0.0.SNAPSHOT");
+ File systemBundleFile = newTmpBundle(bundleItems, bundleName);
+ File replacementBundleFile = newTmpBundle(bundleItems, bundleName, "randomDifference"+Identifiers.makeRandomId(4));
+
+ reuseOsgi = true;
+
+ // Add our bundle, so it feels for all intents and purposes like a "system bundle"
+ Framework reusedFramework = initReusableOsgiFramework();
+ Bundle pseudoSystemBundle = installBundle(reusedFramework, systemBundleFile);
+ reusedBundles.add(pseudoSystemBundle);
+
+ // Launch brooklyn, and explicitly install pre-existing bundle.
+ // Should bring it under brooklyn-management (should not re-install it).
+ BrooklynLauncher launcher = newLauncherForTests(CATALOG_EMPTY_INITIAL);
+ launcher.start();
+ installBrooklynBundle(launcher, replacementBundleFile, force).getWithError();
+
+ assertOnlyBundleReplaced(launcher, bundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher, bundleItems);
+ assertManagedBundle(launcher, bundleName, bundleItems);
+ launcher.terminate();
+
+ // Launch brooklyn again (because will have persisted the pre-installed bundle)
+ BrooklynLauncher launcher2 = newLauncherForTests(CATALOG_EMPTY_INITIAL);
+ launcher2.start();
+ assertOnlyBundleReplaced(reusedFramework, bundleName, pseudoSystemBundle);
+ assertCatalogConsistsOfIds(launcher2, bundleItems);
+ assertManagedBundle(launcher2, bundleName, bundleItems);
+ launcher2.terminate();
}
@Test
@@ -267,4 +432,54 @@ public class BrooklynLauncherRebindCatalogOsgiTest extends AbstractBrooklynLaunc
return framework.getBundleContext().installBundle(bundle.toURI().toString(), stream);
}
}
+
+ private ReferenceWithError<OsgiBundleInstallationResult> installBrooklynBundle(BrooklynLauncher launcher, File bundleFile, boolean force) throws Exception {
+ OsgiManager osgiManager = ((ManagementContextInternal)launcher.getManagementContext()).getOsgiManager().get();
+ try (FileInputStream bundleStream = new FileInputStream(bundleFile)) {
+ return osgiManager.install(null, bundleStream, true, true, force);
+ }
+ }
+
+ private void assertOnlyBundle(BrooklynLauncher launcher, VersionedName bundleName, Bundle expectedBundle) {
+ Framework framework = ((ManagementContextInternal)launcher.getManagementContext()).getOsgiManager().get().getFramework();
+ assertOnlyBundle(framework, bundleName, expectedBundle);
+ }
+
+ private void assertOnlyBundleReplaced(BrooklynLauncher launcher, VersionedName bundleName, Bundle expectedBundle) {
+ Framework framework = ((ManagementContextInternal)launcher.getManagementContext()).getOsgiManager().get().getFramework();
+ assertOnlyBundleReplaced(framework, bundleName, expectedBundle);
+ }
+
+ private void assertOnlyBundle(Framework framework, VersionedName bundleName, Bundle expectedBundle) {
+ List<Bundle> matchingBundles = Osgis.bundleFinder(framework).symbolicName(bundleName.getSymbolicName()).version(bundleName.getOsgiVersionString()).findAll();
+ assertTrue(matchingBundles.contains(expectedBundle), "Bundle missing; matching="+matchingBundles);
+ assertEquals(matchingBundles.size(), 1, "Extra bundles; matching="+matchingBundles);
+ }
+
+ private void assertOnlyBundleReplaced(Framework framework, VersionedName bundleName, Bundle expectedBundle) {
+ List<Bundle> matchingBundles = Osgis.bundleFinder(framework).symbolicName(bundleName.getSymbolicName()).version(bundleName.getOsgiVersionString()).findAll();
+ assertFalse(matchingBundles.contains(expectedBundle), "Bundle still present; matching="+matchingBundles);
+ assertEquals(matchingBundles.size(), 1, "Extra bundles; matching="+matchingBundles);
+ }
+
+ private Framework initReusableOsgiFramework() {
+ if (!reuseOsgi) throw new IllegalStateException("Must first set reuseOsgi");
+
+ if (OsgiManager.tryPeekFrameworkForReuse().isAbsent()) {
+ BrooklynLauncher launcher = newLauncherForTests(CATALOG_EMPTY_INITIAL);
+ launcher.start();
+ launcher.terminate();
+ Os.deleteRecursively(persistenceDir);
+ }
+ return OsgiManager.tryPeekFrameworkForReuse().get();
+ }
+
+ private File newTmpBundle(Set<VersionedName> catalogItems, VersionedName bundleName) {
+ return newTmpBundle(catalogItems, bundleName, null);
+ }
+
+ private File newTmpBundle(Set<VersionedName> catalogItems, VersionedName bundleName, String randomNoise) {
+ String bundleBom = createCatalogYaml(ImmutableList.of(), ImmutableList.of(), catalogItems, randomNoise);
+ return newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, bundleBom.getBytes(StandardCharsets.UTF_8)), bundleName);
+ }
}