fix deserialization to WrappedValue with nested DSL items
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
index a71b79c..88d4ce3 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
@@ -31,12 +31,9 @@
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.DslConfigSupplier;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
-import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 import org.apache.brooklyn.core.resolve.jackson.*;
 import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier;
-import org.apache.brooklyn.core.resolve.jackson.BrooklynRegisteredTypeJacksonSerializationTest.SampleBean;
 import org.apache.brooklyn.core.resolve.jackson.WrappedValuesSerializationTest.ObjectWithWrappedValueString;
-import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.workflow.WorkflowBasicTest;
 import org.apache.brooklyn.entity.stock.BasicStartable;
 import org.apache.brooklyn.test.Asserts;
@@ -301,5 +298,8 @@
         check.accept(r);
         r = BeanWithTypeUtils.convertShallow(mgmt(), MutableMap.of("map", MutableMap.of("a", ws)), TypeToken.of(MapX.class), true, null, true);
         check.accept(r);
+
+        // see deserializeNestedDslIntoWrappedValue test in DslYamlTest
     }
+
 }
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java
index f4e18d6..f54a3e4 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java
@@ -15,6 +15,7 @@
  */
 package org.apache.brooklyn.camp.brooklyn.spi.dsl;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
 import org.apache.brooklyn.api.entity.Entity;
@@ -31,6 +32,8 @@
 import org.apache.brooklyn.core.entity.Dumper;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
+import org.apache.brooklyn.core.resolve.jackson.WrappedValue;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.core.test.entity.TestEntity;
@@ -41,9 +44,12 @@
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.text.StringEscapes;
 import org.testng.annotations.Test;
 
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
 import static org.testng.Assert.assertEquals;
@@ -989,4 +995,25 @@
             return blockingValue;
         }).build()).get();
     }
+
+    @Test
+    public void deserializeNestedDslIntoWrappedValue() throws Exception {
+        final Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName());
+        Entities.submit(app, Tasks.create("test", () -> {
+            try {
+                String literal = StringEscapes.JavaStringEscapes.wrapJavaString("$brooklyn:literal(\"foo\")");
+                WrappedValue<?> ws = BeanWithTypeUtils.newMapper(mgmt(), true, null, true).readerFor(WrappedValue.class)
+                        .readValue(literal);
+                Asserts.assertEquals(ws.get(), "foo");
+
+                String dslExpressionString = "{ \"key\": " + literal + " }";
+                WrappedValue<?> wm = BeanWithTypeUtils.newMapper(mgmt(), true, null, true).readerFor(WrappedValue.class).readValue(dslExpressionString);
+                Asserts.assertEquals(((Map) wm.get()).get("key"), "foo");
+            } catch (Exception e) {
+                throw Exceptions.propagate(e);
+            }
+        })).get();
+    }
 }
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerialization.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerialization.java
index 4fe02fd..4995abc 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerialization.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerialization.java
@@ -43,6 +43,7 @@
 import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.JsonDeserializerForCommonBrooklynThings;
 import org.apache.brooklyn.core.resolve.jackson.BrooklynRegisteredTypeJacksonSerialization.BrooklynRegisteredTypeAndClassNameIdResolver;
 import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.javalang.Reflections;
 import org.slf4j.Logger;
@@ -74,6 +75,9 @@
             if (JsonDeserializerForCommonBrooklynThings.BROOKLYN_PARSE_DSL_FUNCTION!=null && mgmt!= null) {
                 if (looksLikeDsl(v)) {
                     v = JsonDeserializerForCommonBrooklynThings.BROOKLYN_PARSE_DSL_FUNCTION.apply(mgmt, v);
+                } else if (looksLikeNestedDsl(v)) {
+                    Object vDeep = JsonDeserializerForCommonBrooklynThings.BROOKLYN_PARSE_DSL_FUNCTION.apply(mgmt, v);
+                    v = Tasks.resolving(vDeep).as(Object.class).deep();
                 }
             }
             return WrappedValue.of(v);
@@ -91,6 +95,24 @@
             return false;
         }
 
+        private boolean looksLikeNestedDsl(Object v) {
+            if (v instanceof String) {
+                return ((String)v).contains("$brooklyn:");
+            }
+            if (v instanceof Map) {
+                for (Map.Entry entry: ((Map<?, ?>) v).entrySet()) {
+                    if (looksLikeNestedDsl(entry.getKey())) return true;
+                    if (looksLikeNestedDsl(entry.getValue())) return true;
+                }
+            }
+            if (v instanceof Iterable) {
+                for (Object entry: (Iterable)v) {
+                    if (looksLikeNestedDsl(entry)) return true;
+                }
+            }
+            return false;
+        }
+
         Object deserializeWithTypeUnwrapped(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
             List<Exception> exceptions = MutableList.of();
 
diff --git a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerializationTest.java b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerializationTest.java
index b28fbb2..a796b43 100644
--- a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerializationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/WrappedValuesSerializationTest.java
@@ -148,10 +148,11 @@
 
     @Test
     public void testDeserializeUnrecognizedDsl() throws Exception {
-        // tests in CAMP DslDeserializationTest for processing the DSL
         String dslLiteralFoo = "$brooklyn:literal(\"foo\")";
         ObjectWithWrappedValueString impl = deser(json("x: " + dslLiteralFoo), ObjectWithWrappedValueString.class);
         Asserts.assertNotNull(impl.x);
+        // expect the DSL NOT to be recognized by this deserializer as it is not configured with a Brooklyn mgmt context
+        // see tests in CAMP DslSerializationTest for processing the DSL, and DslYamlTest for evaluating the DSL in an entity context
         Asserts.assertEquals(impl.x.get(), dslLiteralFoo);
     }
 }