Repository: brooklyn-server Updated Branches: refs/heads/master 9045005dd -> e73ee2912
WIP - allow types from different bundles if equivalent previously this was only allowed if older bundle was a wrapper, or if it was forced; this could cause problems if such a disallowed addition was forced and then you tried to rebind! now it stores the bundle from which a registered type comes, so we can sensibly have several types. we could even allow them to be different - which would of course cause chaos for users, but if we allow multi-tenant catalogs it will be essential (we simply have to filter for visible types when looking at the registry, and prevent bundle name collisions - eg by prefixing with tenant) though for now we don't allow them to be different. needs testing and test cases still Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/c380e6be Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/c380e6be Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/c380e6be Branch: refs/heads/master Commit: c380e6becf3100311620f569fd77b2f1facbc4a0 Parents: 05d6ee0 Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Authored: Tue Oct 24 17:38:43 2017 +0100 Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Committed: Tue Oct 24 17:38:43 2017 +0100 ---------------------------------------------------------------------- .../core/mgmt/ha/OsgiArchiveInstaller.java | 1 + .../core/typereg/BasicBrooklynTypeRegistry.java | 144 +++++++++++++------ .../core/typereg/BasicRegisteredType.java | 4 +- 3 files changed, 101 insertions(+), 48 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c380e6be/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 f4c2a10..0351838 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 @@ -377,6 +377,7 @@ class OsgiArchiveInstaller { } } if (canUpdate()) { + result.bundle = osgiManager.framework.getBundleContext().getBundle(result.getMetadata().getOsgiUniqueUrl()); if (result.getBundle()==null) { log.warn("Brooklyn thought is was already managing bundle "+result.getMetadata().getVersionedName()+" but it's not installed to framework; reinstalling it"); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c380e6be/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java index 565e3f0..6c14ecf 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java @@ -20,8 +20,10 @@ package org.apache.brooklyn.core.typereg; import java.util.Collection; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -37,8 +39,6 @@ import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext; import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog; import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; -import org.apache.brooklyn.core.mgmt.ha.OsgiManager; -import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; @@ -62,7 +62,7 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { private static final Logger log = LoggerFactory.getLogger(BasicBrooklynTypeRegistry.class); private ManagementContext mgmt; - private Map<String,RegisteredType> localRegisteredTypes = MutableMap.of(); + private Map<String,Map<String,RegisteredType>> localRegisteredTypesAndContainingBundles = MutableMap.of(); public BasicBrooklynTypeRegistry(ManagementContext mgmt) { this.mgmt = mgmt; @@ -76,12 +76,14 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { private Iterable<RegisteredType> getAllWithoutCatalog(Predicate<? super RegisteredType> filter) { // TODO thread safety // TODO optimisation? make indexes and look up? - return Iterables.filter(localRegisteredTypes.values(), filter); + return localRegisteredTypesAndContainingBundles.values().stream(). + flatMap(m -> m.values().stream()).filter(filter::apply).collect(Collectors.toList()); } private Maybe<RegisteredType> getExactWithoutLegacyCatalog(String symbolicName, String version, RegisteredTypeLoadingContext constraint) { // TODO look in any nested/private registries - RegisteredType item = localRegisteredTypes.get(symbolicName+":"+version); + Map<String, RegisteredType> m = localRegisteredTypesAndContainingBundles.get(symbolicName+":"+version); + RegisteredType item = m!=null && !m.isEmpty() ? m.values().iterator().next() : null; return RegisteredTypes.tryValidate(item, constraint); } @@ -368,12 +370,51 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { if (!type.getId().equals(type.getSymbolicName()+":"+type.getVersion())) Asserts.fail("Registered type "+type+" has ID / symname mismatch"); - RegisteredType oldType = mgmt.getTypeRegistry().get(type.getId()); - if (oldType==null || canForce || BrooklynVersionSyntax.isSnapshot(oldType.getVersion())) { - log.debug("Inserting "+type+" into "+this); - localRegisteredTypes.put(type.getId(), type); - } else { - assertSameEnoughToAllowReplacing(oldType, type); + Map<String, RegisteredType> knownMatchingTypesByBundles = localRegisteredTypesAndContainingBundles.get(type.getId()); + if (knownMatchingTypesByBundles!=null && !knownMatchingTypesByBundles.isEmpty()) { + for (RegisteredType existingT: knownMatchingTypesByBundles.values()) { + String reasonForDetailedCheck = null; + if (Objects.equals(existingT.getContainingBundle(), type.getContainingBundle())) { + // they are in the same bundle; is force replacement allowed? + if (canForce) { + log.debug("Addition of "+type+" to replace "+existingT+" allowed because force is on"); + continue; + } + if (BrooklynVersionSyntax.isSnapshot(type.getVersion())) { + if (existingT.getContainingBundle()!=null) { + if (BrooklynVersionSyntax.isSnapshot(VersionedName.fromString(existingT.getContainingBundle()).getVersionString())) { + log.debug("Addition of "+type+" to replace "+existingT+" allowed because both are snapshot"); + continue; + } else { + reasonForDetailedCheck = "the containing bundle "+existingT.getContainingBundle()+" is not a SNAPSHOT and addition is not forced"; + } + } else { + // can this occur? + reasonForDetailedCheck = "the containing bundle of the type is unknown (cannot confirm it is snapshot)"; + } + } else { + reasonForDetailedCheck = "the type is not a SNAPSHOT and addition is not forced"; + } + } else { + reasonForDetailedCheck = type.getId()+" is defined in different bundle"; + } + assertSameEnoughToAllowReplacing(existingT, type, reasonForDetailedCheck); + } + } + + log.debug("Inserting "+type+" into "+this); + Map<String, RegisteredType> m = localRegisteredTypesAndContainingBundles.get(type.getId()); + if (m==null) { + synchronized (localRegisteredTypesAndContainingBundles) { + m = localRegisteredTypesAndContainingBundles.get(type.getId()); + if (m==null) { + m = MutableMap.of(); + localRegisteredTypesAndContainingBundles.put(type.getId(), m); + } + } + } + synchronized (m) { + m.put(type.getContainingBundle(), type); } } @@ -384,7 +425,7 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { * This is needed when replacing an unresolved item with a resolved one or vice versa; * the {@link RegisteredType#equals(Object)} check is too strict. */ - private boolean assertSameEnoughToAllowReplacing(RegisteredType oldType, RegisteredType type) { + private boolean assertSameEnoughToAllowReplacing(RegisteredType oldType, RegisteredType type, String reasonForDetailedCheck) { /* The bundle checksum check prevents swapping a different bundle at the same name+version. * For SNAPSHOT and forced-updates, this method doesn't apply, so we can assume here that * either the bundle checksums are the same, @@ -416,13 +457,13 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { if (!oldType.getVersionedName().equals(type.getVersionedName())) { // different name - shouldn't even come here - throw new IllegalStateException("Cannot add "+type+" to catalog; different "+oldType+" is already present"); + throw new IllegalStateException("Cannot add "+type+" to catalog; different "+oldType+" is already present ("+reasonForDetailedCheck+")"); } if (Objects.equals(oldType.getContainingBundle(), type.getContainingBundle())) { // if named bundles equal then contents must be the same (due to bundle checksum); bail out early if (!samePlan(oldType, type)) { String msg = "Cannot add "+type+" to catalog; different plan in "+oldType+" from same bundle "+ - type.getContainingBundle()+" is already present"; + type.getContainingBundle()+" is already present and "+reasonForDetailedCheck; log.debug(msg+"\n"+ "Plan being added is:\n"+type.getPlan()+"\n"+ "Plan already present is:\n"+oldType.getPlan() ); @@ -430,7 +471,7 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { } if (oldType.getKind()!=RegisteredTypeKind.UNRESOLVED && type.getKind()!=RegisteredTypeKind.UNRESOLVED && !Objects.equals(oldType.getKind(), type.getKind())) { - throw new IllegalStateException("Cannot add "+type+" to catalog; different kind in "+oldType+" from same bundle is already present"); + throw new IllegalStateException("Cannot add "+type+" to catalog; different kind in "+oldType+" from same bundle is already present and "+reasonForDetailedCheck); } return true; } @@ -438,8 +479,7 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { // different bundles, either anonymous or same item in two named bundles if (!samePlan(oldType, type)) { // if plan is different, fail - String msg = "Cannot add "+type+" in "+type.getContainingBundle()+" to catalog; different plan in "+oldType+" from bundle "+ - oldType.getContainingBundle()+" is already present (throwing)"; + String msg = "Cannot add "+type+" to catalog; it is different to "+oldType+", and "+reasonForDetailedCheck+" (throwing)"; log.debug(msg+"\n"+ "Plan being added from "+type.getContainingBundle()+" is:\n"+type.getPlan()+"\n"+ "Plan already present from "+oldType.getContainingBundle()+" is:\n"+oldType.getPlan() ); @@ -448,45 +488,31 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { if (oldType.getKind()!=RegisteredTypeKind.UNRESOLVED && type.getKind()!=RegisteredTypeKind.UNRESOLVED && !Objects.equals(oldType.getKind(), type.getKind())) { // if kind is different and both resolved, fail - throw new IllegalStateException("Cannot add "+type+" in "+type.getContainingBundle()+" to catalog; different kind in "+oldType+" from bundle "+ - oldType.getContainingBundle()+" is already present"); + throw new IllegalStateException("Cannot add "+type+" to catalog; it is a different kind to "+oldType+", and " + + reasonForDetailedCheck); } - // now if old is a wrapper bundle (or old, no bundle), allow it -- metadata may be different here - // but we'll allow that, probably the user is updating their catalog to the new format. - // icons might change, maybe a few other things (but any such errors can be worked around), - // and more useful to be able to upload the same BOM or replace an anonymous BOM with a named bundle. - // crucially if old is a wrapper bundle the containing bundle won't actually be needed for anything, - // so this is safe in terms of search paths etc. - if (oldType.getContainingBundle()==null) { - // if old type wasn't from a bundle, let it be replaced by a bundle - return true; - } - // bundle is changing; was old bundle a wrapper? - OsgiManager osgi = ((ManagementContextInternal)mgmt).getOsgiManager().orNull(); - if (osgi==null) { - // shouldn't happen, as we got a containing bundle, but just in case - return true; - } - if (BasicBrooklynCatalog.isNoBundleOrSimpleWrappingBundle(mgmt, - osgi.getManagedBundle(VersionedName.fromString(oldType.getContainingBundle())))) { - // old was a wrapper bundle; allow it - return true; - } - - throw new IllegalStateException("Cannot add "+type+" in "+type.getContainingBundle()+" to catalog; " - + "item is already present in different bundle "+oldType.getContainingBundle()); + // it is the same plan as a type in another bundle; + // previously we allowed only when the other bundle was a wrapper or no bundle + // (or it was forced - which was guaranteed to cause problems on rebind!); + // but now we always allow it, and we track which bundles things come from + return true; } private boolean samePlan(RegisteredType oldType, RegisteredType type) { return RegisteredTypes.arePlansEquivalent(oldType, type); } + /** removes registered type with the given ID from the local in-memory catalog + * (ignores bundle, deletes from all bundles if present in multiple; + * falls back to removing from legacy catalog if not known here in any bundles) + * @throws NoSuchElementException if not found */ @Beta // API stabilising public void delete(VersionedName type) { - RegisteredType registeredTypeRemoved = localRegisteredTypes.remove(type.toString()); - if (registeredTypeRemoved != null) { - return ; + synchronized (localRegisteredTypesAndContainingBundles) { + if (localRegisteredTypesAndContainingBundles.remove(type.toString()) != null) { + return; + } } // legacy deletion (may call back to us, but max once) @@ -498,10 +524,34 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { // throw new NoSuchElementException("No catalog item found with id "+type); } + /** removes the given registered type in the noted bundle; + * if not known in that bundle tries deleting from legacy catalog. + * @throws NoSuchElementException if not found */ public void delete(RegisteredType type) { - delete(type.getVersionedName()); + Map<String, RegisteredType> m = localRegisteredTypesAndContainingBundles.get(type.getId()); + if (m!=null) { + RegisteredType removedItem = m.remove(type.getContainingBundle()); + if (m.isEmpty()) { + synchronized (localRegisteredTypesAndContainingBundles) { + m = localRegisteredTypesAndContainingBundles.get(type.getId()); + if (m!=null && m.isEmpty()) { + localRegisteredTypesAndContainingBundles.remove(type.getId()); + } else { + // either removed in parallel or no longer empty + } + } + if (removedItem==null) { + throw new NoSuchElementException("Requested to delete "+type+" from "+type.getContainingBundle()+", " + + "but that type was not known in that bundle, it is in "+m.keySet()+" instead"); + } + } + } else { + // try legacy delete + delete(type.getVersionedName()); + } } + /** as {@link #delete(VersionedName)} */ @Beta // API stabilising public void delete(String id) { delete(VersionedName.fromString(id)); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c380e6be/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java index f76a9f4..8bb0908 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java @@ -30,6 +30,7 @@ import org.apache.brooklyn.util.collections.MutableSet; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.javalang.JavaClassNames; import org.apache.brooklyn.util.osgi.VersionedName; +import org.apache.brooklyn.util.text.Strings; import com.google.common.annotations.Beta; import com.google.common.base.Objects; @@ -155,9 +156,10 @@ public class BasicRegisteredType implements RegisteredType { @Override public String toString() { return JavaClassNames.simpleClassName(this)+"["+getId()+ + (Strings.isNonBlank(getContainingBundle()) ? ";"+getContainingBundle() : "")+ (isDisabled() ? ";DISABLED" : "")+ (isDeprecated() ? ";deprecated" : "")+ - (getPlan()!=null ? ";"+getPlan().getPlanFormat() : "")+ + (getPlan()!=null && getPlan().getPlanFormat()!=null ? ";"+getPlan().getPlanFormat() : "")+ "]"; }