Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/868#discussion_r147096369
  
    --- Diff: 
core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java
 ---
    @@ -368,13 +399,77 @@ public void 
addToLocalUnpersistedTypeRegistry(RegisteredType type, boolean canFo
             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);
    -        }
    +        Locks.withLock(localRegistryLock.writeLock(),
    +            () -> {
    +                Map<String, RegisteredType> knownMatchingTypesByBundles = 
localRegisteredTypesAndContainingBundles.get(type.getId());
    +                if (knownMatchingTypesByBundles==null) {
    +                    knownMatchingTypesByBundles = MutableMap.of();
    +                    
localRegisteredTypesAndContainingBundles.put(type.getId(), 
knownMatchingTypesByBundles);
    +                }
    +
    +                Set<String> oldContainingBundlesToRemove = MutableSet.of();
    +                boolean newIsWrapperBundle = 
isWrapperBundle(type.getContainingBundle());
    +                for (RegisteredType existingT: 
knownMatchingTypesByBundles.values()) {
    +                    String reasonForDetailedCheck = null;
    +                    boolean sameBundle = 
Objects.equals(existingT.getContainingBundle(), type.getContainingBundle());
    +                    boolean oldIsWrapperBundle = 
isWrapperBundle(existingT.getContainingBundle());
    +                    if (sameBundle || (oldIsWrapperBundle && 
newIsWrapperBundle)) {
    +                        // allow replacement (different plan for same 
type) if either
    +                        // it's the same bundle or the old one was a 
wrapper, AND
    +                        // either we're forced or in snapshot-land
    +                        if (!sameBundle) {
    +                            // if old is wrapper bundle, we have to to 
remove the old record
    +                            
oldContainingBundlesToRemove.add(existingT.getContainingBundle());
    +                        }
    +                        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 if (oldIsWrapperBundle) {
    +                        reasonForDetailedCheck = type.getId()+" is in a 
named bundle replacing an item from an anonymous bundle-wrapped BOM, so 
definitions must be the same (or else give it a different version)";
    +                    } else if (newIsWrapperBundle) {
    +                        reasonForDetailedCheck = type.getId()+" is in an 
anonymous bundle-wrapped BOM replacing an item from a named bundle, so 
definitions must be the same (or else give it a different version)";
    +                    } else {
    +                        reasonForDetailedCheck = type.getId()+" is defined 
in different bundle";
    +                    }
    +                    assertSameEnoughToAllowReplacing(existingT, type, 
reasonForDetailedCheck);
    +                }
    +            
    +                log.debug("Inserting "+type+" into "+this+
    +                    (oldContainingBundlesToRemove.isEmpty() ? "" : " 
(removing entry from "+oldContainingBundlesToRemove+")"));
    +                for (String oldContainingBundle: 
oldContainingBundlesToRemove) {
    +                    
knownMatchingTypesByBundles.remove(oldContainingBundle);
    --- End diff --
    
    This is hard to follow - it looks reasonable, but it's hard to reason about 
because there are so many situations. Also the context/granularity of how this 
is used feels strange - other code like 
`BasicBrooklynCatalog.collectCatalogItemsFromItemMetadataBlock` is calling this 
repeatedly for each thing in a bundle (. If something goes wrong, it's then up 
to the caller to revert those actions. But this call side-effects what we had 
recorded against for other bundles. So hard to see how we get back to the 
previous state on such a rollback.
    
    I come back to the idea of making `bundle: ` compulsory. That would 
simplify this code.
    
    Longer term, I also think we want a "working view" of the 
`BrooklynTypeRegistry` that can apply a bunch of changes and then commit or 
abort them. But that's a bigger topic of conversation (completely separate from 
this PR!).


---

Reply via email to