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 2014/11/04 15:30:07 UTC

[13/18] OSGi catalog item test (see full msg for details)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
new file mode 100644
index 0000000..83cd46d
--- /dev/null
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package io.brooklyn.camp.brooklyn.catalog;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import io.brooklyn.camp.brooklyn.AbstractYamlTest;
+
+import java.util.Collection;
+
+import org.testng.annotations.Test;
+
+import brooklyn.catalog.CatalogItem;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.BasicEntity;
+import brooklyn.management.osgi.OsgiStandaloneTest;
+import brooklyn.management.osgi.OsgiTestResources;
+
+import com.google.common.collect.Iterables;
+
+
+public class CatalogYamlEntityTest extends AbstractYamlTest {
+    
+    private static final String SIMPLE_ENTITY_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY;
+
+    @Test
+    public void testAddCatalogItem() throws Exception {
+        String registeredTypeName = "my.catalog.app.id.load";
+        addCatalogOSGiEntity(registeredTypeName);
+
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName);
+        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationReferencingCatalog() throws Exception {
+        String registeredTypeName = "my.catalog.app.id.launch";
+        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+    }
+
+    @Test
+    public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
+        String referencedRegisteredTypeName = "my.catalog.app.id.referenced";
+        String referrerRegisteredTypeName = "my.catalog.app.id.referring";
+        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
+        addCatalogOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
+
+        String yaml = "name: simple-app-yaml\n" +
+                      "location: localhost\n" +
+                      "services: \n" +
+                      "  - serviceType: "+referrerRegisteredTypeName;
+        Entity app = createAndStartApplication(yaml);
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
+
+        deleteCatalogEntity(referencedRegisteredTypeName);
+        deleteCatalogEntity(referrerRegisteredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationChildWithCatalogReferencingOtherCatalog() throws Exception {
+        String referencedRegisteredTypeName = "my.catalog.app.id.child.referenced";
+        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
+        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
+        addCatalogChildOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
+
+        Entity app = createAndStartApplication(
+            "name: simple-app-yaml",
+            "location: localhost",
+            "services:",
+            "- serviceType: "+BasicEntity.class.getName(),
+            "  brooklyn.children:",
+            "  - type: " + referrerRegisteredTypeName);
+
+        Collection<Entity> children = app.getChildren();
+        assertEquals(children.size(), 1);
+        Entity child = Iterables.getOnlyElement(children);
+        assertEquals(child.getEntityType().getName(), BasicEntity.class.getName());
+        Collection<Entity> grandChildren = child.getChildren();
+        assertEquals(grandChildren.size(), 1);
+        Entity grandChild = Iterables.getOnlyElement(grandChildren);
+        assertEquals(grandChild.getEntityType().getName(), BasicEntity.class.getName());
+        Collection<Entity> grandGrandChildren = grandChild.getChildren();
+        assertEquals(grandGrandChildren.size(), 1);
+        Entity grandGrandChild = Iterables.getOnlyElement(grandGrandChildren);
+        assertEquals(grandGrandChild.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
+
+        deleteCatalogEntity(referencedRegisteredTypeName);
+        deleteCatalogEntity(referrerRegisteredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationWithTypeUsingJavaColonPrefix() throws Exception {
+        String registeredTypeName = SIMPLE_ENTITY_TYPE;
+        String serviceName = "java:"+SIMPLE_ENTITY_TYPE;
+        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
+    }
+
+    @Test
+    public void testLaunchApplicationLoopWithJavaTypeName() throws Exception {
+        String registeredTypeName = SIMPLE_ENTITY_TYPE;
+        String serviceName = SIMPLE_ENTITY_TYPE;
+        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
+    }
+
+    @Test
+    public void testLaunchApplicationChildLoopCatalogIdFails() throws Exception {
+        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
+        try {
+            addCatalogChildOSGiEntity(referrerRegisteredTypeName, referrerRegisteredTypeName);
+            fail("Expected to throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            assertTrue(e.getMessage().contains("Could not find "+referrerRegisteredTypeName));
+        }
+    }
+
+    private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) throws Exception {
+        addCatalogOSGiEntity(registeredTypeName, serviceType);
+        String yaml = "name: simple-app-yaml\n" +
+                      "location: localhost\n" +
+                      "services: \n" +
+                      "  - serviceType: "+registeredTypeName;
+        Entity app = createAndStartApplication(yaml);
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    private void addCatalogOSGiEntity(String registeredTypeName) {
+        addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+    }
+
+    private void addCatalogOSGiEntity(String registeredTypeName, String serviceType) {
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + registeredTypeName,
+            "  name: My Catalog App",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + serviceType);
+    }
+
+    private void addCatalogChildOSGiEntity(String registeredTypeName, String serviceType) {
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + registeredTypeName,
+            "  name: My Catalog App",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + BasicEntity.class.getName(),
+            "  brooklyn.children:",
+            "  - type: " + serviceType);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
new file mode 100644
index 0000000..0d5c44d
--- /dev/null
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package io.brooklyn.camp.brooklyn.catalog;
+
+import static org.testng.Assert.assertEquals;
+import io.brooklyn.camp.brooklyn.AbstractYamlTest;
+
+import org.testng.annotations.Test;
+
+import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogPredicates;
+import brooklyn.entity.Entity;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.management.osgi.OsgiStandaloneTest;
+import brooklyn.policy.Policy;
+
+import com.google.common.collect.Iterables;
+
+public class CatalogYamlPolicyTest extends AbstractYamlTest {
+    private static final String SIMPLE_POLICY_TYPE = "brooklyn.osgi.tests.SimplePolicy";
+    private static final String SIMPLE_ENTITY_TYPE = "brooklyn.osgi.tests.SimpleEntity";
+
+    @Test
+    public void testAddCatalogItem() throws Exception {
+        assertEquals(countCatalogPolicies(), 0);
+
+        String registeredTypeName = "my.catalog.policy.id.load";
+        addCatalogOSGiPolicy(registeredTypeName, SIMPLE_POLICY_TYPE);
+
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName);
+        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
+        assertEquals(countCatalogPolicies(), 1);
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationReferencingPolicy() throws Exception {
+        String registeredTypeName = "my.catalog.policy.id.launch";
+        addCatalogOSGiPolicy(registeredTypeName, SIMPLE_POLICY_TYPE);
+        Entity app = createAndStartApplication(
+            "name: simple-app-yaml",
+            "location: localhost",
+            "services: ",
+            "  - type: brooklyn.entity.basic.BasicEntity\n" +
+            "    brooklyn.policies:\n" +
+            "    - type: " + registeredTypeName,
+            "      brooklyn.config:",
+            "        config2: config2 override",
+            "        config3: config3");
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        Policy policy = Iterables.getOnlyElement(simpleEntity.getPolicies());
+        assertEquals(policy.getPolicyType().getName(), SIMPLE_POLICY_TYPE);
+        assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config1")), "config1");
+        assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config2")), "config2 override");
+        assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config3")), "config3");
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
+        String referencedRegisteredTypeName = "my.catalog.policy.id.referenced";
+        String referrerRegisteredTypeName = "my.catalog.policy.id.referring";
+        addCatalogOSGiPolicy(referencedRegisteredTypeName, SIMPLE_POLICY_TYPE);
+
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + referrerRegisteredTypeName,
+            "  name: My Catalog App",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + SIMPLE_ENTITY_TYPE,
+            "  brooklyn.policies:",
+            "  - type: " + referencedRegisteredTypeName);
+
+        String yaml = "name: simple-app-yaml\n" +
+                      "location: localhost\n" +
+                      "services: \n" +
+                      "  - serviceType: "+referrerRegisteredTypeName;
+
+        Entity app = createAndStartApplication(yaml);
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        Policy policy = Iterables.getOnlyElement(simpleEntity.getPolicies());
+        assertEquals(policy.getPolicyType().getName(), SIMPLE_POLICY_TYPE);
+
+        deleteCatalogEntity(referencedRegisteredTypeName);
+    }
+
+    private void addCatalogOSGiPolicy(String registeredTypeName, String serviceType) {
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + registeredTypeName,
+            "  name: My Catalog Policy",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "brooklyn.policies:",
+            "- type: " + serviceType,
+            "  brooklyn.config:",
+            "    config1: config1",
+            "    config2: config2");
+    }
+
+    private int countCatalogPolicies() {
+        return Iterables.size(mgmt().getCatalog().getCatalogItems(CatalogPredicates.IS_POLICY));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-called-v1-osgi-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-called-v1-osgi-catalog.yaml b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-called-v1-osgi-catalog.yaml
new file mode 100644
index 0000000..058dc33
--- /dev/null
+++ b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-called-v1-osgi-catalog.yaml
@@ -0,0 +1,10 @@
+
+services:
+- type: brooklyn.osgi.tests.more.MoreEntity
+
+brooklyn.catalog:
+  id: more-entity-v1
+  version: 1.0
+  # see OsgiTestResources
+  libraries:
+  - classpath:/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-osgi-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-osgi-catalog.yaml b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-osgi-catalog.yaml
new file mode 100644
index 0000000..574a35e
--- /dev/null
+++ b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-osgi-catalog.yaml
@@ -0,0 +1,10 @@
+
+services:
+- type: brooklyn.osgi.tests.more.MoreEntity
+
+brooklyn.catalog:
+  id: more-entity
+  version: 1.0
+  # see OsgiTestResources
+  libraries:
+  - classpath:/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml
new file mode 100644
index 0000000..c1ab77d
--- /dev/null
+++ b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml
@@ -0,0 +1,12 @@
+
+services:
+- type: brooklyn.osgi.tests.more.MoreEntity
+  brooklyn.policies:
+  - type: simple-policy
+
+brooklyn.catalog:
+  id: more-entity
+  version: 1.0
+  # see OsgiTestResources
+  libraries:
+  - classpath:/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml
new file mode 100644
index 0000000..4f102b5
--- /dev/null
+++ b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml
@@ -0,0 +1,11 @@
+
+services:
+- type: brooklyn.osgi.tests.more.MoreEntity
+
+brooklyn.catalog:
+  id: more-entity
+  version: 2.0
+  # see OsgiTestResources
+  libraries:
+  - classpath:/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar
+  - classpath:/brooklyn/osgi/brooklyn-test-osgi-entities.jar

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/simple-policy-osgi-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/simple-policy-osgi-catalog.yaml b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/simple-policy-osgi-catalog.yaml
new file mode 100644
index 0000000..b3f407c
--- /dev/null
+++ b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/simple-policy-osgi-catalog.yaml
@@ -0,0 +1,10 @@
+
+brooklyn.policies:
+- type: brooklyn.osgi.tests.SimplePolicy
+
+brooklyn.catalog:
+  id: simple-policy
+  version: 1.0
+  # see OsgiTestResources
+  libraries:
+  - classpath:/brooklyn/osgi/brooklyn-test-osgi-entities.jar

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/utils/common/src/main/java/brooklyn/util/exceptions/Exceptions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/exceptions/Exceptions.java b/utils/common/src/main/java/brooklyn/util/exceptions/Exceptions.java
index d7282d1..10126e3 100644
--- a/utils/common/src/main/java/brooklyn/util/exceptions/Exceptions.java
+++ b/utils/common/src/main/java/brooklyn/util/exceptions/Exceptions.java
@@ -126,22 +126,36 @@ public class Exceptions {
         return collapse(source, true);
     }
     
+    /** as {@link #collapse(Throwable)} but includes causal messages in the message as per {@link #collapseTextIncludingAllCausalMessages(Throwable)};
+     * use with care (limit once) as repeated usage can result in multiple copies of the same message */ 
+    public static Throwable collapseIncludingAllCausalMessages(Throwable source) {
+        return collapse(source, true, true);
+    }
+    
     /** creates (but does not throw) a new {@link PropagatedRuntimeException} whose 
      * message is taken from the first _interesting_ element in the source,
      * and optionally also the causal chain */
     public static Throwable collapse(Throwable source, boolean collapseCausalChain) {
+        return collapse(source, collapseCausalChain, false);
+    }
+    
+    private static Throwable collapse(Throwable source, boolean collapseCausalChain, boolean includeAllCausalMessages) {
         String message = "";
         Throwable collapsed = source;
         int collapseCount = 0;
+        boolean messageIsFinal = false;
         // remove boring stack traces at the head
-        while (isBoring(collapsed)) {
+        while (isBoring(collapsed)  && !messageIsFinal) {
             collapseCount++;
             Throwable cause = collapsed.getCause();
             if (cause==null)
                 // everything in the tree is boring...
                 return source;
             String collapsedS = collapsed.getMessage();
-            if (Strings.isNonBlank(collapsedS)) {
+            if (collapsed instanceof PropagatedRuntimeException && ((PropagatedRuntimeException)collapsed).isCauseEmbeddedInMessage()) {
+                message = collapsed.getMessage();
+                messageIsFinal = true;
+            } else if (Strings.isNonBlank(collapsedS)) {
                 collapsedS = Strings.removeFromEnd(collapsedS, cause.toString(), stripBoringPrefixes(cause.toString()), cause.getMessage());
                 collapsedS = stripBoringPrefixes(collapsedS);
                 if (Strings.isNonBlank(collapsedS))
@@ -161,14 +175,19 @@ public class Exceptions {
             messagesCause = messagesCause.getCause();
         }
         
-        if (collapseCount==0)
+        if (collapseCount==0 && !includeAllCausalMessages)
             return source;
         
+        if (collapseCount==0) {
+            message = messagesCause.toString();
+            messagesCause = messagesCause.getCause();
+        }
+        
         if (Strings.isBlank(message)) {
             return new PropagatedRuntimeException(collapseCausalChain ? collapsed : source);
         } else {
-            if (messagesCause!=null) {
-                String extraMessage = collapseText(messagesCause);
+            if (messagesCause!=null && !messageIsFinal) {
+                String extraMessage = collapseText(messagesCause, includeAllCausalMessages);
                 message = appendSeparator(message, extraMessage);
             }
             return new PropagatedRuntimeException(message, collapseCausalChain ? collapsed : source, true);
@@ -195,8 +214,19 @@ public class Exceptions {
 
     /** like {@link #collapse(Throwable)} but returning a one-line message suitable for logging without traces */
     public static String collapseText(Throwable t) {
+        return collapseText(t, false);
+    }
+
+    /** normally {@link #collapseText(Throwable)} will stop following causal chains when encountering an interesting exception
+     * with a message; this variant will continue to follow such causal chains, showing all messages. 
+     * for use e.g. when verbose is desired in the single-line message. */
+    public static String collapseTextIncludingAllCausalMessages(Throwable t) {
+        return collapseText(t, true);
+    }
+    
+    private static String collapseText(Throwable t, boolean includeAllCausalMessages) {
         if (t == null) return null;
-        Throwable t2 = collapse(t);
+        Throwable t2 = collapse(t, true, includeAllCausalMessages);
         if (t2 instanceof PropagatedRuntimeException) {
             if (((PropagatedRuntimeException)t2).isCauseEmbeddedInMessage())
                 // normally
@@ -205,10 +235,20 @@ public class Exceptions {
                 return ""+t2.getCause();
             return ""+t2.getClass();
         }
-        return t2.toString();
+        String result = t2.toString();
+        if (!includeAllCausalMessages) {
+            return result;
+        }
+        Throwable cause = t2.getCause();
+        if (cause != null) {
+            String causeResult = collapseText(new PropagatedRuntimeException(cause));
+            if (result.indexOf(causeResult)>=0)
+                return result;
+            return result + "; caused by "+causeResult;
+        }
+        return result;
     }
 
-
     public static RuntimeException propagate(Collection<? extends Throwable> exceptions) {
         throw propagate(create(exceptions));
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
index e24c475..dcbd9da 100644
--- a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
+++ b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
@@ -21,7 +21,6 @@ package brooklyn.util.guava;
 import java.util.concurrent.Callable;
 
 import brooklyn.util.guava.IfFunctions.IfFunctionBuilderApplyingFirst;
-import brooklyn.util.javalang.Enums;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35fd6341/utils/common/src/main/java/brooklyn/util/guava/IllegalStateExceptionSupplier.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/guava/IllegalStateExceptionSupplier.java b/utils/common/src/main/java/brooklyn/util/guava/IllegalStateExceptionSupplier.java
index 14ee639..b75bca2 100644
--- a/utils/common/src/main/java/brooklyn/util/guava/IllegalStateExceptionSupplier.java
+++ b/utils/common/src/main/java/brooklyn/util/guava/IllegalStateExceptionSupplier.java
@@ -29,7 +29,7 @@ public class IllegalStateExceptionSupplier implements Supplier<RuntimeException>
     
     public IllegalStateExceptionSupplier() { this(null, null); }
     public IllegalStateExceptionSupplier(String message) { this(message, null); }
-    public IllegalStateExceptionSupplier(Throwable cause) { this(null, cause); }
+    public IllegalStateExceptionSupplier(Throwable cause) { this(cause!=null ? cause.getMessage() : null, cause); }
     public IllegalStateExceptionSupplier(String message, Throwable cause) { 
         this.message = message;
         this.cause = cause;