more tests, warning messages, prep for upgrading bundles and types

Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/9173183f
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/9173183f
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/9173183f

Branch: refs/heads/master
Commit: 9173183f247add9a99316c6f5276b28a45429d62
Parents: d64ca16
Author: Alex Heneveld <[email protected]>
Authored: Mon Oct 30 22:38:03 2017 +0000
Committer: Alex Heneveld <[email protected]>
Committed: Tue Oct 31 01:07:44 2017 +0000

----------------------------------------------------------------------
 .../brooklyn/api/mgmt/ManagementContext.java    |  3 +-
 .../CatalogOsgiVersionMoreEntityRebindTest.java |  2 +-
 .../mgmt/internal/LocalManagementContext.java   |  1 +
 .../mgmt/rebind/RebindExceptionHandlerImpl.java |  2 +-
 .../core/mgmt/rebind/RebindIteration.java       | 44 +++++++---
 .../core/typereg/BundleUpgradeParser.java       | 87 +++++++++++++++++++-
 .../AbstractBrooklynLauncherRebindTest.java     | 18 +++-
 .../BrooklynLauncherRebindCatalogOsgiTest.java  | 72 +++++++++++++---
 .../BrooklynLauncherUpgradeCatalogOsgiTest.java | 48 +++++++++++
 9 files changed, 246 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
----------------------------------------------------------------------
diff --git 
a/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java 
b/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
index 2c615ed..4ad9718 100644
--- a/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
+++ b/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
@@ -243,7 +243,8 @@ public interface ManagementContext {
     @Beta  // see comment on isRunning() as items might move to a status 
handler
     public boolean isStartupComplete();
 
-    /** Returns node state, always reporting {@link 
ManagementNodeState#INITIALIZING} if there is any transition.
+    /** Returns node state, always reporting {@link 
ManagementNodeState#INITIALIZING} if there is any transition
+     * and {@link ManagementNodeState#FAILED} if there are any server errors.
      * If this returns {@link ManagementNodeState#MASTER} we can guarantee the 
node to be in master state,
      * unlike {@link HighAvailabilityManager#getNodeState()} which may return 
{@link ManagementNodeState#MASTER} slightly early. 
      */

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
index 64cd99e..835609d 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
@@ -274,7 +274,7 @@ public class CatalogOsgiVersionMoreEntityRebindTest extends 
AbstractYamlRebindTe
             Asserts.shouldHaveFailedPreviously("Expected deployment to fail 
rebind; instead got "+app2);
         } catch (Exception e) {
             // should fail to rebind this entity
-            Asserts.expectedFailureContainsIgnoreCase(e, more.getId(), "unable 
to load", BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
+            Asserts.expectedFailureContainsIgnoreCase(e, more.getId(), 
"class", BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, "not found");
         }
     }
     

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
index d6c69d4..e5af9b1 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
@@ -453,6 +453,7 @@ public class LocalManagementContext extends 
AbstractManagementContext {
     public ManagementNodeState getNodeState() {
         synchronized (startupSynchObject) {
             if (!startupComplete) return ManagementNodeState.INITIALIZING;
+            if (!errors().isEmpty()) return ManagementNodeState.FAILED;
             return getHighAvailabilityManager().getNodeState();
         }
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindExceptionHandlerImpl.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindExceptionHandlerImpl.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindExceptionHandlerImpl.java
index 17d4073..794161b 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindExceptionHandlerImpl.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindExceptionHandlerImpl.java
@@ -242,7 +242,7 @@ public class RebindExceptionHandlerImpl implements 
RebindExceptionHandler {
         if (danglingRefFailureMode == 
RebindManager.RebindFailureMode.FAIL_FAST) {
             throw new IllegalStateException("No catalog item found with id 
"+id);
         } else {
-            warn("No catalog item found with id "+id+"; dangling reference on 
rebind");
+            warn("No catalog item found with id "+id+"; dangling reference on 
rebind (subsequent log messages will indicate context)");
             return null;
         }
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/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 8a667eb..4751341 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
@@ -970,10 +970,15 @@ public abstract class RebindIteration {
         }
         
         @SuppressWarnings("unchecked")
+        // TODO should prefer a registered type as the type to load (in lieu 
of jType),
+        // but note some callers (enrichers etc) use catalogItemId to be the 
first entry in search path rather than their actual type,
+        // so until callers are all updated all we can do here is load the 
java type with no guarantee the catalogItemId should be the same.
+        // (yoml should help a lot with this.)
         protected <T extends BrooklynObject> LoadedClass<? extends T> 
load(Class<T> bType, String jType,
                 String catalogItemId, List<String> searchPath, String 
contextSuchAsId) {
             checkNotNull(jType, "Type of %s (%s) must not be null", 
contextSuchAsId, bType.getSimpleName());
 
+            List<String> warnings = MutableList.of();
             List<String> reboundSearchPath = MutableList.of();
             if (searchPath != null && !searchPath.isEmpty()) {
                 for (String searchItemId : searchPath) {
@@ -987,8 +992,7 @@ public abstract class RebindIteration {
                     if (fixedSearchItemId != null) {
                         reboundSearchPath.add(fixedSearchItemId);
                     } else {
-                        LOG.warn("Unable to load catalog item "+ searchItemId
-                            + " for search path of "+contextSuchAsId + " (" + 
bType.getSimpleName()+"); attempting to load "+jType+" nevertheless");
+                        warnings.add("unable to resolve search path entry "+ 
searchItemId);
                     }
                 }
             }
@@ -1016,23 +1020,31 @@ public abstract class RebindIteration {
                         return new LoadedClass<T>(loader.loadClass(jType, 
bType), transformedCatalogItemId, reboundSearchPath);
                     } catch (Exception e) {
                         Exceptions.propagateIfFatal(e);
-                        LOG.warn("Unable to load class "+jType+" needed for 
"+catalogItemId+" for "+contextSuchAsId+", via "+transformedCatalogItemId+" 
loader (will try reflections)");
+                        warnings.add("unable to load class "+jType+" for 
resovled context type "+transformedCatalogItemId);
                     }
                 } else {
-                    LOG.warn("Unable to load catalog item "+catalogItemId+" 
("+bType+") for " + contextSuchAsId +
-                      " ("+bType.getSimpleName()+"); will try reflection");
+                    // TODO fail, rather than fallback to java?
+                    warnings.add("unable to resolve context type 
"+catalogItemId);
                 }
+            } else {
+                // can happen for enrichers etc added by java, and for 
BasicApplication when things are deployed;
+                // no need to warn
             }
 
             try {
-                return new LoadedClass<T>((Class<T>)loadClass(jType), 
catalogItemId, reboundSearchPath);
+                Class<T> jTypeC = (Class<T>)loadClass(jType);
+                if (!warnings.isEmpty()) {
+                    LOG.warn("Loaded java type "+jType+" for 
"+bType.getSimpleName().toLowerCase()+" "+contextSuchAsId+" but had errors: 
"+Strings.join(warnings, ";"));
+                }
+                return new LoadedClass<T>(jTypeC, catalogItemId, 
reboundSearchPath);
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);
-                LOG.warn("Unable to load class "+jType+" needed for 
"+catalogItemId+" for "+contextSuchAsId+", via reflections (may try others, 
will throw if fails)");
             }
 
             if (catalogItemId != null) {
-                throw new IllegalStateException("Unable to load "+jType+" for 
catalog item " + catalogItemId + " for " + contextSuchAsId);
+                String msg = "Class "+jType+" not found for 
"+bType.getSimpleName().toLowerCase()+" "+contextSuchAsId+" 
("+catalogItemId+"): "+Strings.join(warnings, ";");
+                LOG.warn(msg+" (rethrowing)");
+                throw new IllegalStateException(msg);
                 
             } else if 
(BrooklynFeatureEnablement.isEnabled(FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND))
 {
                 //Try loading from whichever catalog bundle succeeds (legacy 
CI items only; also disabling this, as no longer needed 2017-09)
@@ -1041,15 +1053,21 @@ public abstract class RebindIteration {
                     BrooklynClassLoadingContext catalogLoader = 
CatalogUtils.newClassLoadingContext(managementContext, item);
                     Maybe<Class<?>> catalogClass = 
catalogLoader.tryLoadClass(jType);
                     if (catalogClass.isPresent()) {
-                        LOG.warn("Found "+jType+" only by scanning catalog 
item search paths");
+                        LOG.warn("Falling back to java type "+jType+" for 
"+bType.getSimpleName().toLowerCase()+" "+contextSuchAsId+" using catalog 
search paths, found on "+item+
+                            (warnings.isEmpty() ? "" : ", after errors: 
"+Strings.join(warnings, ";")));
                         return new LoadedClass<T>((Class<? extends T>) 
catalogClass.get(), catalogItemId, reboundSearchPath);
                     }
                 }
-                throw new IllegalStateException("No catalogItemId specified 
for " + contextSuchAsId +
-                    " and can't load class (" + jType + ") from either 
classpath or catalog items");
+                String msg = "Class "+jType+" not found for 
"+bType.getSimpleName().toLowerCase()+" "+contextSuchAsId+", even after legacy 
global classpath search"+
+                    (warnings.isEmpty() ? "" : ": "+Strings.join(warnings, 
";"));
+                LOG.warn(msg+" (rethrowing)");
+                throw new IllegalStateException(msg);
+
             } else {
-                throw new IllegalStateException("No catalogItemId specified 
for " + contextSuchAsId +
-                    " and can't load class (" + jType + ") from classpath");
+                String msg = "Class "+jType+" not found for 
"+bType.getSimpleName().toLowerCase()+" "+contextSuchAsId+
+                    (warnings.isEmpty() ? "" : ": "+Strings.join(warnings, 
";"));
+                LOG.warn(msg+" (rethrowing)");
+                throw new IllegalStateException(msg);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/core/src/main/java/org/apache/brooklyn/core/typereg/BundleUpgradeParser.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/typereg/BundleUpgradeParser.java 
b/core/src/main/java/org/apache/brooklyn/core/typereg/BundleUpgradeParser.java
index e797137..bfac6c6 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/typereg/BundleUpgradeParser.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/typereg/BundleUpgradeParser.java
@@ -23,8 +23,10 @@ import static 
com.google.common.base.Preconditions.checkNotNull;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Dictionary;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.brooklyn.api.catalog.CatalogItem;
@@ -65,8 +67,8 @@ public class BundleUpgradeParser {
      *       version 1.0.0 and its catalog.bom contains items "foo" and "bar", 
then it is equivalent
      *       to writing {@code "foo:[0,1.0.0)","bar:[0,1.0.0)"}.
      *       As per the comments on {@link 
#MANIFEST_HEADER_FORCE_REMOVE_BUNDLES},
-     *       the version of the bundle and items being added here to replace 
legacy catalog items
-     *       should typically be larger in major/minor/point value 
+     *       the version of the bundle and items being added by a bundle to 
replace legacy catalog items
+     *       should typically be larger in major/minor/point value,
      *       as a qualifier bump can be quite complex due to ordering 
differences.  
      * </ul>
      */
@@ -106,7 +108,84 @@ public class BundleUpgradeParser {
      */
     @Beta
     public static final String MANIFEST_HEADER_FORCE_REMOVE_BUNDLES = 
"brooklyn-catalog-force-remove-bundles";
-    
+
+    /**
+     * A header in a bundle's manifest, indicating that this bundle recommends 
a set of upgrades.
+     * These will be advisory unless the bundle being upgraded is 
force-removed in which case it will be applied automatically
+     * wherever the bundle is in use.
+     * 
+     * The format is a comma separate list of {@code key=value} pairs, where 
each key and value is a name with a version range
+     * (as per {@link #MANIFEST_HEADER_FORCE_REMOVE_BUNDLES}). The {@code 
=value} can be omitted, and usually is,
+     * to mean this bundle at this version. (The {@code =value} is available 
if one bundle is defining upgrades for other bundles.)  
+     * 
+     * A wildcard can be given as the key, without a version ({@code *}) or 
with ({@code *:[0,1)}) to refer to 
+     * all previous versions (OSGi ordering, see below) or the indicated 
versions, respectively, of the bundle this manifest is defining.
+     * 
+     * If this header is supplied and {@link 
#MANIFEST_HEADER_UPGRADE_FOR_TYPES} is omitted, the types to upgrade are 
inferred as 
+     * being all types defined in the bundle this manifest is defining, and 
all versions of this bundle upgraded by this bundle
+     * according to this header. If this header is included but does not 
declare any versions of this bundle upgraded by this bundle, 
+     * then {@link #MANIFEST_HEADER_UPGRADE_FOR_TYPES} must be explicitly set.
+     * 
+     * Version ordering may be surprising particularly in regard to qualifiers.
+     * See {@link #MANIFEST_HEADER_FORCE_REMOVE_BUNDLES} for discussion of 
this.
+     * 
+     * <p>
+     * 
+     * In most use cases, one can provide a single line, e.g. if releasing a 
v2.0.0:
+     * 
+     * {@code brooklyn-catalog-upgrade-for-bundles: *:[0,2)}
+     * 
+     * to indicate that this bundle is able to upgrade all instances of this 
bundle lower than 2.0.0,
+     * and all types in this bundle will upgrade earlier types.
+     * 
+     * This can be used in conjunction with:
+     * 
+     * {@code brooklyn-catalog-force-remove-bundles: *:[0,2)}
+     * 
+     * to forcibly remove older bundles at an appropriate point (eg a restart) 
and cause all earlier instances of the bundle
+     * and the type instances they contain to be upgraded with the same-named 
types in the 2.0.0 bundle.
+     * 
+     * It is not necessary to supply {@link 
#MANIFEST_HEADER_UPGRADE_FOR_TYPES} unless types are being renamed
+     * or versions are not in line with previous versions of this bundle.
+     */
+    @Beta
+    public static final String MANIFEST_HEADER_UPGRADE_FOR_BUNDLES = 
"brooklyn-catalog-upgrade-for-bundles";
+
+    /**
+     * A header in a bundle's manifest, indicating that this bundle recommends 
a set of upgrades.
+     * These will be advisory unless the type being upgraded is force-removed 
in which case it will be applied automatically
+     * wherever the type is in use.
+     * 
+     * The format is as per {@link #MANIFEST_HEADER_UPGRADE_FOR_BUNDLES}, with 
two differences in interpretation.
+     * 
+     * If {@code =value} is omitted, the ugprade target is assumed to be the 
same type as the corresonding key but at the
+     * version of the bundle.
+     * 
+     * A wildcard can be given as the key, without a version ({@code *}) or 
with ({@code *:[0,1)}) to refer to
+     * all types in the present bundle.
+     * 
+     * If the version/range for a type is omitted in any key, it is inferred 
as the versions of this bundle 
+     * that this bundle declares it can upgrade in the {@link 
#MANIFEST_HEADER_UPGRADE_FOR_BUNDLES} header.
+     * It is an error to omit a version/range if that header is not present or 
does not declare versions
+     * of the same bundle being upgraded.  
+     * 
+     * If this key is omitted, then the default is {@code *} if a {@link 
#MANIFEST_HEADER_UPGRADE_FOR_BUNDLES} header
+     * is present and empty if that header is not present.
+     * 
+     * What this is saying in most cases is that if a bundle {@code foo:1} 
contains {@code foo-node:1}, and 
+     * bundle {@code foo:2} contains {@code foo-node:2}, then:
+     * if {@code foo:2} declares {@code brooklyn-catalog-upgrade-for-bundles: 
foo:1} it will also declare that
+     * {@code foo-node:2} upgrades {@code foo-node:1};
+     * if {@code foo:2} declares {@code brooklyn-catalog-upgrade-for-bundles: 
*} the same thing will occur
+     * (and it would also upgrade a {@code foo:0} contains {@code foo-node:0});
+     * if {@code foo:2} declares no {@code brooklyn-catalog-upgrade} manifest 
headers, then no advisory
+     * upgrades will be noted.
+     * 
+     * As noted in {@link #MANIFEST_HEADER_UPGRADE_FOR_BUNDLES} the primary 
use case for this header is type renames.
+     */
+    @Beta
+    public static final String MANIFEST_HEADER_UPGRADE_FOR_TYPES = 
"brooklyn-catalog-upgrade-for-types";
+
     /**
      * The result from parsing bundle(s) to find their upgrade info.
      */
@@ -116,6 +195,8 @@ public class BundleUpgradeParser {
         public static class Builder {
             private Set<VersionRangedName> removedLegacyItems = new 
LinkedHashSet<>();
             private Set<VersionRangedName> removedBundles = new 
LinkedHashSet<>();
+            private Map<VersionedName,VersionRangedName> upgradeBundles = new 
LinkedHashMap<>();
+            private Map<VersionedName,VersionRangedName> upgradeTypes = new 
LinkedHashMap<>();
 
             public Builder removedLegacyItems(Collection<VersionRangedName> 
vals) {
                 removedLegacyItems.addAll(vals);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/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 474c72b..461dce7 100644
--- 
a/launcher/src/test/java/org/apache/brooklyn/launcher/AbstractBrooklynLauncherRebindTest.java
+++ 
b/launcher/src/test/java/org/apache/brooklyn/launcher/AbstractBrooklynLauncherRebindTest.java
@@ -41,15 +41,22 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.objs.BrooklynObjectType;
 import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry;
 import org.apache.brooklyn.api.typereg.ManagedBundle;
 import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.CampTypePlanTransformer;
 import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
+import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.persist.PersistMode;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
 import org.apache.brooklyn.entity.stock.BasicEntity;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.ResourceUtils;
@@ -214,7 +221,7 @@ public abstract class AbstractBrooklynLauncherRebindTest {
     }
     
     protected void assertManagedBundle(BrooklynLauncher launcher, 
VersionedName bundleId, Set<VersionedName> expectedCatalogItems) {
-        assertNotNull(findManagedBundle(launcher, bundleId), bundleId+" not 
found");
+        assertNotNull(findManagedBundle(launcher, bundleId), "Bundle 
"+bundleId+" not found");
         
         Set<VersionedName> actualCatalogItems = new LinkedHashSet<>();
         Iterable<RegisteredType> types = 
launcher.getManagementContext().getTypeRegistry().getAll();
@@ -402,4 +409,13 @@ public abstract class AbstractBrooklynLauncherRebindTest {
             launcher.terminate();
         }
     }
+    
+    public static Application createAndStartApplication(ManagementContext 
mgmt, String input) throws Exception {
+        EntitySpec<?> spec = 
+            
mgmt.getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, 
input, RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
+        final Entity app = mgmt.getEntityManager().createEntity(spec);
+        app.invoke(Startable.START, MutableMap.of()).get();
+        return (Application) app;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/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 119f341..b67a29e 100644
--- 
a/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogOsgiTest.java
+++ 
b/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogOsgiTest.java
@@ -30,16 +30,20 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState;
 import org.apache.brooklyn.api.objs.BrooklynObjectType;
+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.core.mgmt.rebind.RebindTestUtils;
+import org.apache.brooklyn.core.typereg.BundleUpgradeParser;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.test.support.TestResourceUnavailableException;
 import org.apache.brooklyn.util.core.osgi.Osgis;
@@ -49,6 +53,8 @@ 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.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.launch.Framework;
 import org.testng.Assert;
@@ -140,6 +146,10 @@ public abstract class 
BrooklynLauncherRebindCatalogOsgiTest extends AbstractBroo
     }
     
     protected void startT2(BrooklynLauncher l) {
+        startT2(l, true);
+    }
+    
+    protected void startT2(BrooklynLauncher l, boolean assertSuccess) {
         if (launcherT2!=null) throw new IllegalStateException("Already started 
T2 launcher");
         
         try {
@@ -153,7 +163,6 @@ public abstract class BrooklynLauncherRebindCatalogOsgiTest 
extends AbstractBroo
             l.highAvailabilityMode(HighAvailabilityMode.HOT_STANDBY);
         }
         l.start();
-        Framework f = 
((ManagementContextInternal)l.getManagementContext()).getOsgiManager().get().getFramework();
         launcherLast = launcherT2 = l;
         if (isT1KeptRunningWhenT2Starts()) {
             assertHotStandbyNow(launcherT2);
@@ -162,9 +171,14 @@ public abstract class 
BrooklynLauncherRebindCatalogOsgiTest extends AbstractBroo
         if (startupAssertions!=null) startupAssertions.run();
     }
     protected void promoteT2IfStandby() {
+        promoteT2IfStandby(true);
+    }
+    protected void promoteT2IfStandby(boolean assertSuccess) {
         if (isT1KeptRunningWhenT2Starts()) {
             launcherT1.terminate();
-            assertMasterEventually(launcherT2);
+            if (assertSuccess) {
+                assertMasterEventually(launcherT2);
+            }
             
             if (startupAssertions!=null) startupAssertions.run();
         }
@@ -449,8 +463,10 @@ public abstract class 
BrooklynLauncherRebindCatalogOsgiTest extends AbstractBroo
     }
 
     protected String getBundleSupplyingFirstType(Set<VersionedName> 
bundleItems) {
-        return VersionedName.fromString( 
launcherLast.getManagementContext().getTypeRegistry().get(
-            bundleItems.iterator().next().toString() ).getContainingBundle() 
).getVersionString();
+        RegisteredType type = 
launcherLast.getManagementContext().getTypeRegistry().get(
+            bundleItems.iterator().next().toString() );
+        if (type==null) return null;
+        return VersionedName.fromString( type.getContainingBundle() 
).getVersionString();
     }
 
     @Test
@@ -599,12 +615,34 @@ public abstract class 
BrooklynLauncherRebindCatalogOsgiTest extends AbstractBroo
             Assert.assertNotEquals(bundlePersistenceId1, bundlePersistenceId2);
         }
     }
-    
-    // TODO removed item in deployment fails - rebind and upgrade uses new item
-    // TODO removed item in deployment upgrades - rebind and upgrade uses new 
item
-    // TODO removed item in spec fails
-    // TODO removed item in spec upgrades
-    // TODO removed item deployed after rebind
+
+    @Test
+    // might change this to fail in future, see RebindIteration.load
+    public void testRebindRemovedItemWorksIfJavaClassFound() throws Exception {
+        Set<VersionedName> bundleItemsV1 = 
ImmutableSet.of(VersionedName.fromString("one:1.0.0"));
+        String bundleBomV1 = createCatalogYaml(ImmutableList.of(), 
bundleItemsV1);
+        VersionedName bundleNameV1 = new 
VersionedName("org.example.testRebindGetsInitialOsgiCatalog", "1.0.0");
+        File bundleFileV1 = 
newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, 
bundleBomV1.getBytes(StandardCharsets.UTF_8)), bundleNameV1);
+        File initialBomFileV1 = 
newTmpFile(createCatalogYaml(ImmutableList.of(bundleFileV1.toURI()), 
ImmutableList.of()));
+
+        Set<VersionedName> bundleItemsV2 = 
ImmutableSet.of(VersionedName.fromString("one:2.0.0"));
+        String bundleBomV2 = createCatalogYaml(ImmutableList.of(), 
bundleItemsV2);
+        VersionedName bundleNameV2 = new 
VersionedName("org.example.testRebindGetsInitialOsgiCatalog", "2.0.0");
+        File bundleFileV2 = 
newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, 
bundleBomV2.getBytes(StandardCharsets.UTF_8)), bundleNameV2,
+            
ImmutableMap.of(BundleUpgradeParser.MANIFEST_HEADER_FORCE_REMOVE_BUNDLES, "*"));
+        File initialBomFileV2 = 
newTmpFile(createCatalogYaml(ImmutableList.of(bundleFileV2.toURI()), 
ImmutableList.of()));
+        
+        startT1(newLauncherForTests(initialBomFileV1.getAbsolutePath()));
+        Application app = 
createAndStartApplication(launcherLast.getManagementContext(), 
+            "services: [ { type: 'one:1.0.0' } ]");
+
+        // should start and promote fine, but entity's type shouldn't be 
loadable (and there will be warnings)
+        startT2(newLauncherForTests(initialBomFileV2.getAbsolutePath()));
+        promoteT2IfStandby();
+        
+        Entity entity = Iterables.getOnlyElement( 
Iterables.getOnlyElement(launcherLast.getManagementContext().getApplications()).getChildren()
 );
+        Assert.assertEquals(entity.getCatalogItemId(), 
bundleItemsV1.iterator().next());
+    }
     
     protected void 
assertPersistedBundleListingEqualsEventually(BrooklynLauncher launcher, 
Set<VersionedName> bundles) {
         Asserts.succeedsEventually(new Runnable() {
@@ -703,5 +741,17 @@ public abstract class 
BrooklynLauncherRebindCatalogOsgiTest extends AbstractBroo
         String bundleBom = createCatalogYaml(ImmutableList.of(), 
ImmutableList.of(), catalogItems, randomNoise);
         return newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, 
bundleBom.getBytes(StandardCharsets.UTF_8)), bundleName);
     }
-    
+
+    public static void main(String[] args) throws Exception {
+        try {
+            BrooklynLauncherRebindCatalogOsgiTest fixture = new 
LauncherRebindSubTests();
+            fixture.setUp();
+            fixture.testRebindRemovedItemWorksIfJavaClassFound();
+            fixture.tearDown();
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+        } 
+        System.exit(0);
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9173183f/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherUpgradeCatalogOsgiTest.java
----------------------------------------------------------------------
diff --git 
a/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherUpgradeCatalogOsgiTest.java
 
b/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherUpgradeCatalogOsgiTest.java
index a6f5129..289b96a 100644
--- 
a/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherUpgradeCatalogOsgiTest.java
+++ 
b/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherUpgradeCatalogOsgiTest.java
@@ -21,21 +21,26 @@ package org.apache.brooklyn.launcher;
 import static 
org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog.CATALOG_BOM;
 import static 
org.apache.brooklyn.core.typereg.BundleUpgradeParser.MANIFEST_HEADER_FORCE_REMOVE_BUNDLES;
 import static 
org.apache.brooklyn.core.typereg.BundleUpgradeParser.MANIFEST_HEADER_FORCE_REMOVE_LEGACY_ITEMS;
+import static 
org.apache.brooklyn.core.typereg.BundleUpgradeParser.MANIFEST_HEADER_UPGRADE_FOR_BUNDLES;
 
 import java.io.File;
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
 
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.util.osgi.VersionedName;
+import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 
 public class BrooklynLauncherUpgradeCatalogOsgiTest extends 
AbstractBrooklynLauncherRebindTest {
 
@@ -144,4 +149,47 @@ public class BrooklynLauncherUpgradeCatalogOsgiTest 
extends AbstractBrooklynLaun
         assertManagedBundle(launcher, bundleVersionedName, 
ImmutableSet.<VersionedName>of(one_1_0_0));
         launcher.terminate();
     }
+    
+    // removed item with upgrade deployed after rebind
+    // TODO WIP
+    @Test
+    public void testForceUpgradeItemByRemovingBundle() throws Exception {
+        VersionedName one_1_0_0 = VersionedName.fromString("one:1.0.0");
+        VersionedName one_2_0_0 = VersionedName.fromString("one:2.0.0");
+        
+        String bundleSymbolicName = "org.example.testForceUpgradeBundle";
+        VersionedName bundleVersionedName1 = new 
VersionedName(bundleSymbolicName, "1.0.0");
+        String bundleBom1 = createCatalogYaml(ImmutableList.<URI>of(), 
ImmutableSet.<VersionedName>of(one_1_0_0));
+        File bundleFile1 = 
newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, 
bundleBom1.getBytes(StandardCharsets.UTF_8)), bundleVersionedName1);
+
+        newPersistedStateInitializer()
+                .bundle(bundleVersionedName1, bundleFile1)
+                .initState();
+        
+        VersionedName bundleVersionedName2 = new 
VersionedName(bundleSymbolicName, "2.0.0");
+        String bundleBom2 = createCatalogYaml(ImmutableList.<URI>of(), 
ImmutableSet.<VersionedName>of(one_2_0_0));
+        Map<String, String> bundleManifest2 = ImmutableMap.of(
+            MANIFEST_HEADER_FORCE_REMOVE_BUNDLES, 
"\""+bundleSymbolicName+":[0.0.0,2.0.0)\"",
+            MANIFEST_HEADER_UPGRADE_FOR_BUNDLES, "*");
+        File bundleFile2 = 
newTmpBundle(ImmutableMap.of(BasicBrooklynCatalog.CATALOG_BOM, 
bundleBom2.getBytes(StandardCharsets.UTF_8)), bundleVersionedName2, 
bundleManifest2);
+
+        File initialBomFile = 
newTmpFile(createCatalogYaml(ImmutableList.of(bundleFile2.toURI()), 
ImmutableList.of()));
+
+        BrooklynLauncher launcher = 
newLauncherForTests(initialBomFile.getAbsolutePath());
+        launcher.start();
+
+        Application app = 
createAndStartApplication(launcher.getManagementContext(), 
+            "services: [ { type: 'one:1.0.0' } ]");
+        Entity one = Iterables.getOnlyElement( app.getChildren() );
+        Assert.assertEquals(one.getCatalogItemId(), "one:2.0.0");
+        
+        launcher.terminate();
+    }
+        
+    // NB other related tests in BrooklynLauncherRebindCatalogOsgiTest:
+    // * removed item in deployment fails - rebind and upgrade uses new item
+    // * removed item in deployment upgrades - rebind and upgrade uses new item
+    // * removed item in spec fails
+    // * removed item in spec upgrades
+    
 }

Reply via email to