This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit e59f4f2cb1f91b2cc78df4124814066aa8dc3ffc
Author: Alex Heneveld <[email protected]>
AuthorDate: Tue Jun 20 01:56:45 2023 +0100

    add json shorthand deserializer
    
    and make sure it correctly instantiates things that are converted shallowly
---
 .../brooklyn/spi/dsl/DslSerializationTest.java     |  83 ++++++++-
 .../resolve/jackson/JsonShorthandDeserializer.java | 177 +++++++++++++++++++
 .../jackson/WrappedValuesSerialization.java        |   8 +-
 .../JacksonJsonShorthandDeserializerTest.java      | 189 +++++++++++++++++++++
 .../jackson/WrappedValuesSerializationTest.java    |  11 +-
 5 files changed, 452 insertions(+), 16 deletions(-)

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 3ee4bda07c..f331bc588e 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
@@ -18,11 +18,11 @@
  */
 package org.apache.brooklyn.camp.brooklyn.spi.dsl;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.google.common.base.Optional;
-import java.util.Map;
-import java.util.function.Supplier;
-
+import com.google.common.reflect.TypeToken;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
@@ -30,11 +30,10 @@ import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
-import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
-import org.apache.brooklyn.core.resolve.jackson.MapperTestFixture;
-import org.apache.brooklyn.core.resolve.jackson.WrappedValue;
-import org.apache.brooklyn.core.resolve.jackson.WrappedValuesSerializationTest;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.resolve.jackson.*;
 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;
@@ -45,6 +44,10 @@ import 
org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
 @Test
 public class DslSerializationTest extends AbstractYamlTest implements 
MapperTestFixture {
 
@@ -229,4 +232,70 @@ public class DslSerializationTest extends AbstractYamlTest 
implements MapperTest
         impl = deser(json("asu: { type: "+BasicStartable.class.getName()+" 
}"), ObjectWithWrappedValueSpec.class);
         Asserts.assertNotNull(impl.asu);
     }
+
+
+    static class ObjectWithTypedMaps {
+        Map<String,String> s;
+        Map<String,WrappedValue<String>> w;
+        Map<String,Object> o;
+    }
+
+    @Test
+    public void testDeserializeDslToMap() throws Exception {
+        String unwrappedDesiredValue = "foo";
+        String dslExpressionString = "$brooklyn:literal(" +
+                JavaStringEscapes.wrapJavaString(unwrappedDesiredValue)
+                + ")";
+        Object dslExpressionSupplier = 
DslUtils.resolveBrooklynDslValue(dslExpressionString, null, mgmt(), null).get();
+
+        ObjectWithTypedMaps impl;
+
+        MutableMap<String, MutableMap<String, Object>> dslMap = 
MutableMap.of("w", MutableMap.of("x", dslExpressionSupplier));
+
+        impl = BeanWithTypeUtils.convertShallow(mgmt(), dslMap, 
TypeToken.of(ObjectWithTypedMaps.class), true, null, true);
+        Asserts.assertInstanceOf(impl.w.get("x").getSupplier(), 
BrooklynDslDeferredSupplier.class);
+
+        impl = BeanWithTypeUtils.convertDeeply(mgmt(), dslMap, 
TypeToken.of(ObjectWithTypedMaps.class), true, null, true);
+        Asserts.assertInstanceOf(impl.w.get("x").getSupplier(), 
BrooklynDslDeferredSupplier.class);
+    }
+
+
+    // from JsonShorthandDeserializer
+    public static class MapT<T> {
+        @JsonDeserialize(contentUsing = JsonShorthandDeserializer.class)
+        Map<String,T> map;
+        void setMap(Map<String,T> map) { this.map = map; }
+    }
+
+    public static class MapX extends MapT<X> {}
+
+    public static class X {
+        WrappedValue<Object> w;
+
+        @JsonShorthandDeserializer.JsonShorthandInstantiator
+        public static X fromShorthand(WrappedValue<Object> o) {
+            X x = new X();
+            x.w = o;
+            return x;
+        }
+    }
+
+    @Test
+    public void convertShorthandShallowOrDeep() throws JsonProcessingException 
{
+        Consumer<MapX> check = r -> {
+            Asserts.assertInstanceOf(r.map.get("a").w.getSupplier(), 
BrooklynDslDeferredSupplier.class);
+            //Asserts.assertEquals(r.map.get("a").w.get(), "vv");  // not in 
an entity; above check is good enough, will fail if the map doesn't actually 
hold the wrapped value
+        };
+
+        String unwrappedDesiredValue = "foo";
+        String dslExpressionString = "$brooklyn:literal(" +
+                JavaStringEscapes.wrapJavaString(unwrappedDesiredValue)
+                + ")";
+        Object ws = DslUtils.resolveBrooklynDslValue(dslExpressionString, 
null, mgmt(), null).get();
+        MapX r;
+        r = BeanWithTypeUtils.convertDeeply(mgmt(), MutableMap.of("map", 
MutableMap.of("a", ws)), TypeToken.of(MapX.class), true, null, true);
+        check.accept(r);
+        r = BeanWithTypeUtils.convertShallow(mgmt(), MutableMap.of("map", 
MutableMap.of("a", ws)), TypeToken.of(MapX.class), true, null, true);
+        check.accept(r);
+    }
 }
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonShorthandDeserializer.java
 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonShorthandDeserializer.java
new file mode 100644
index 0000000000..6bd5d8e4a4
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonShorthandDeserializer.java
@@ -0,0 +1,177 @@
+/*
+ * 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 org.apache.brooklyn.core.resolve.jackson;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+import com.google.common.reflect.TypeToken;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.javalang.coerce.TryCoercer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/** Deserializer which can be set on a field in a bean to indicate that a 
special
+ * {@link JsonShorthandInstantiator}-annotated static 1-arg method should be 
used to instantiate the object
+ * from another type if primary instantiation does not succeed.
+ */
+public class JsonShorthandDeserializer extends JsonDeserializer<Object> 
implements ContextualDeserializer {
+
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface JsonShorthandInstantiator {}
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(JsonShorthandDeserializer.class);
+    private final JavaType type;
+
+    public JsonShorthandDeserializer() {
+        type = null;
+    }
+
+    public JsonShorthandDeserializer(JavaType type) {
+        this.type = type;
+    }
+
+    // inefficient, and unnecessary normally as the shorthand is only from 
json, and it is quite flexible
+//    static {
+//        TypeCoercions.registerAdapter("80-json-shorthand", new TryCoercer() {
+//            @Override
+//            public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> type) {
+//                Optional<Method> shorthand = 
Arrays.stream(type.getRawType().getMethods()).filter(m -> 
m.getAnnotation(JsonShorthandInstantiator.class) != null).findFirst();
+//                if (!shorthand.isPresent()) return null;
+//                try {
+//                    return Maybe.of((T) shorthand.get().invoke(null, input));
+//                } catch (Exception e) {
+//                    return Maybe.absent("Unable to coerce 
"+input.getClass()+" to "+type+" using json shorthand");
+//                }
+//            }
+//        });
+//    }
+
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, 
BeanProperty property) {
+        JavaType t;
+        if (property!=null) {
+            t = property.getType();
+        } else {
+            t = ctxt.getContextualType();
+        }
+        if (t==null) {
+            // shorthand not supported - could warn
+            return this;
+        }
+        if (t instanceof MapLikeType) {
+            return new JsonShorthandDeserializer(t.getContentType() );
+        }
+        if (Collection.class.isAssignableFrom(t.getRawClass())) {
+            return new JsonShorthandDeserializer(t.getContentType() );
+        }
+        return new JsonShorthandDeserializer(t);
+    }
+
+    // TODO do these methods need the BJSU routines?
+
+    @Override
+    public Object deserializeWithType(JsonParser p, DeserializationContext 
ctxt, TypeDeserializer typeDeserializer) throws IOException {
+        TokenBuffer b = new TokenBuffer(p, ctxt);
+        b.copyCurrentStructure(p);
+        Object r1 = null;
+        try {
+            r1 = super.deserializeWithType(b.asParserOnFirstToken(), ctxt, 
typeDeserializer);
+        } catch (Exception e) {
+            // ignore if "with type" deserialization didn't work here; it was 
probably incompatible for assigning _to_ X
+            // the type will be re-read and instantiated, and assigned to the 
_value_ of X
+        }
+        if (r1!=null && type.getRawClass().isInstance(r1)) return r1;
+        return deserialize(b.asParserOnFirstToken(), ctxt);
+    }
+
+    @Override
+    public Object deserialize(JsonParser p, DeserializationContext ctxt) 
throws IOException {
+        if (type==null) {
+            // shorthand only supported where specified on a property and type 
is determinable from context
+            return 
ctxt.findNonContextualValueDeserializer(ctxt.constructType(Object.class)).deserialize(p,
 ctxt);
+        }
+
+        List<Exception> exceptions = MutableList.of();
+        try {
+//            TokenBuffer b = 
BrooklynJacksonSerializationUtils.createBufferForParserCurrentObject(p, ctxt);
+//            // above might be better, then access with 
createParserFromTokenBufferAndParser
+            TokenBuffer b = new TokenBuffer(p, ctxt);
+            b.copyCurrentStructure(p);
+
+            try {
+                Object r1 = 
ctxt.findNonContextualValueDeserializer(type).deserialize(b.asParserOnFirstToken(),
 ctxt);
+                if (r1!=null && type.getRawClass().isInstance(r1)) return r1;
+            } catch (Exception e) {
+                exceptions.add(e);
+            }
+
+            try {
+                Method inst = Arrays.stream(type.getRawClass().getMethods())
+                        .filter(m -> 
m.getAnnotation(JsonShorthandInstantiator.class) != null).findAny()
+//                        .orElseThrow(() -> new IllegalStateException("No 
public method annotated @JsonShorthandInstantiator"));
+                        .orElseThrow(() -> {
+                            return new IllegalStateException("No public method 
annotated @JsonShorthandInstantiator");
+                        });
+                if ((inst.getModifiers() & Modifier.STATIC)==0) throw new 
IllegalStateException("@JsonShorthandInstantiator method must be static: 
"+inst);
+                if (inst.getParameterCount()!=1) throw new 
IllegalStateException("@JsonShorthandInstantiator method should take a single 
argument: "+inst);
+                Object v = 
ctxt.findRootValueDeserializer(ctxt.constructType(inst.getParameters()[0].getParameterizedType())).deserialize(b.asParserOnFirstToken(),
 ctxt);
+                if (v instanceof Map || (v instanceof WrappedValue && 
((WrappedValue)v).getSupplier()==null && ((WrappedValue)v).get() instanceof 
Map)) {
+                    // possibly we are unable to instantiate the type using 
the above; most of the time it is preferred, but in some cases we need the 
below,
+                    // eg to handle DSL expressions in wrapped values (but 
many times the below fails). see JacksonJsonShorthandDeserializerTest failures 
when either is disallowed.
+                    Object v2 = 
ctxt.findNonContextualValueDeserializer(ctxt.constructType(inst.getParameters()[0].getParameterizedType())).deserialize(b.asParserOnFirstToken(),
 ctxt);
+                    if (!(v2 instanceof Map)) {
+                        v = v2;
+                    }
+                }
+                return inst.invoke(null, v);
+            } catch (Exception e) {
+                exceptions.add(e);
+            }
+
+            throw new IllegalStateException("Cannot instantiate as longhand or 
shorthand: " + exceptions, exceptions.stream().findFirst().orElse(null));
+        } finally {
+            if (!exceptions.isEmpty() && LOG.isTraceEnabled()) {
+                LOG.trace("Exceptions encountered while deserializing: " + 
exceptions);
+                exceptions.forEach(e -> LOG.trace("- ", e));
+            }
+        }
+
+    }
+
+}
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 8a05b79a64..4fe02fd157 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
@@ -93,7 +93,10 @@ public class WrappedValuesSerialization {
 
         Object deserializeWithTypeUnwrapped(JsonParser p, 
DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws 
IOException {
             List<Exception> exceptions = MutableList.of();
+
             try {
+//                TokenBuffer b = new TokenBuffer(p, ctxt);
+//                b.copyCurrentStructure(p);
                 TokenBuffer b = 
BrooklynJacksonSerializationUtils.createBufferForParserCurrentObject(p, ctxt);
                 JavaType genericType = null;
                 try {
@@ -108,6 +111,8 @@ public class WrappedValuesSerialization {
                     try {
                         // this uses our type deserializer, will try type 
instantiation from a declared type and/or expected type of the generics
                         return 
ctxt.findRootValueDeserializer(genericType).deserialize(
+                                // createParserFromTokenBufferAndParser(b, p)
+                                // should we use line above instead of line 
below, which we use several lines further below?
                                 b.asParserOnFirstToken(), ctxt);
                     } catch (Exception e) {
                         exceptions.add(e);
@@ -118,7 +123,8 @@ public class WrappedValuesSerialization {
                     try {
                         // this does _not_ use our type deserializer; will try 
type instantiation from the expected type of the generics however
                         return 
ctxt.findNonContextualValueDeserializer(genericType).deserialize(
-                                createParserFromTokenBufferAndParser(b, p), 
ctxt);
+                                createParserFromTokenBufferAndParser(b, p),
+                                ctxt);
                     } catch (Exception e) {
                         exceptions.add(e);
                     }
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/JacksonJsonShorthandDeserializerTest.java
 
b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/JacksonJsonShorthandDeserializerTest.java
new file mode 100644
index 0000000000..a74f9c3133
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/JacksonJsonShorthandDeserializerTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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 org.apache.brooklyn.core.resolve.jackson;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.common.reflect.TypeToken;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.Jsonya;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.yaml.Yamls;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+public class JacksonJsonShorthandDeserializerTest {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(JacksonJsonShorthandDeserializerTest.class);
+
+    static class X0 {
+        @JsonDeserialize(using = JsonShorthandDeserializer.class)
+        X x;
+    }
+
+    static class MapT<T> {
+        @JsonDeserialize(contentUsing = JsonShorthandDeserializer.class)
+        Map<String,T> map;
+        void setMap(Map<String,T> map) { this.map = map; }
+    }
+
+    static class MapX extends MapT<X> {}
+
+    static class X {
+
+//        @JsonCreator
+        X()          {        o = "noa"; }
+//        @JsonCreator
+//        X(String x)  { v = x; o = "str"; }
+//        @JsonCreator
+//        X(Integer x) { v = x; o = "int"; }
+
+        Object v;
+        String o = "orig";
+
+        @JsonShorthandDeserializer.JsonShorthandInstantiator
+        public static X fromShorthand(Object o) {
+            X x = new X();
+            x.v = o;
+            x.o = "sho";
+            return x;
+        }
+    }
+
+    static class V {
+        static V of(int v) {
+            V result = new V();
+            result.v = v;
+            return result;
+        }
+        Object v;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            V v1 = (V) o;
+            return Objects.equals(v, v1.v);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(v);
+        }
+    }
+
+    @Test
+    public void testX() throws JsonProcessingException {
+        check("foo", "foo", "sho");
+        check(""+1, 1, "sho");
+        check("{ type: "+ V.class.getName()+", v: 2 }", V.of(2), "sho");  // 
or obj?
+        check("{ v: 3 }", 3, "noa");
+        check("{ u: 4 }", MutableMap.of("u", 4), "sho");
+        check("{ type: "+X.class.getName()+", v: 5 }", 5, "noa");  // or obj?
+    }
+
+    private static void check(String xJson, Object v, String o) throws 
JsonProcessingException {
+        X0 x0 = BeanWithTypeUtils.newMapper(null, false, null, 
true).readerFor(Object.class).readValue(
+                Jsonya.newInstance().add(Yamls.parseAll(Strings.lines(
+                        "type: "+X0.class.getName(),
+                        "x: "+xJson
+                )).iterator().next()).toString()
+        );
+
+        Assert.assertEquals(x0.x.v, v);
+        Assert.assertEquals(x0.x.o, o);
+
+
+        MapX xm = BeanWithTypeUtils.newMapper(null, false, null, 
true).readerFor(MapX.class).readValue(
+                Jsonya.newInstance().add(Yamls.parseAll(Strings.lines(
+                        "map:",
+                        "  x: "+xJson
+                )).iterator().next()).toString()
+        );
+
+        Assert.assertEquals(xm.map.get("x").v, v);
+        Assert.assertEquals(xm.map.get("x").o, o);
+    }
+
+    public static class DS implements DeferredSupplier<String> {
+        String s;
+        public String get() { return s; }
+    }
+
+    public static class W {
+        WrappedValue<String> w;
+
+        @JsonShorthandDeserializer.JsonShorthandInstantiator
+        public static W fromShorthand(WrappedValue<String> o) {
+            W w = new W();
+            w.w = o;
+            return w;
+        }
+    }
+
+    public static class MWH {
+        MapT<W> holder;
+    }
+
+    @Test
+    public void convertShallowOrDeep() throws JsonProcessingException {
+        Consumer<MapX> check = r -> {
+            Asserts.assertEquals(r.map.get("a").v, "vv");
+            Asserts.assertEquals(r.map.get("a").o, "sho");
+        };
+
+        MapX r;
+
+        r = BeanWithTypeUtils.convertDeeply(null, MutableMap.of("map", 
MutableMap.of("a", "vv")), TypeToken.of(MapX.class), false, null, false);
+        check.accept(r);
+        r = BeanWithTypeUtils.convertShallow(null, MutableMap.of("map", 
MutableMap.of("a", "vv")), TypeToken.of(MapX.class), false, null, false);
+        check.accept(r);
+
+        DS ds = new DS(); ds.s = "vv";
+        ObjectMapper mapper = BeanWithTypeUtils.newMapper(null, false, null, 
true);
+        MapT<W> mw = new MapT<>();
+        W w = W.fromShorthand(WrappedValue.of(ds));
+        mw.setMap(MutableMap.of("a", w));
+        MWH h = new MWH();
+        h.holder = mw;
+        String hs = mapper.writeValueAsString(h);
+        Asserts.assertEquals(hs, 
"{\"holder\":{\"map\":{\"a\":{\"w\":{\"type\":\"org.apache.brooklyn.core.resolve.jackson.JacksonJsonShorthandDeserializerTest$DS\",\"s\":\"vv\"}}}}}");
+        MWH h2 = mapper.readValue(hs, MWH.class);
+        Asserts.assertEquals(h2.holder.map.get("a").w.get(), "vv");
+
+        LocalManagementContext mgmt = 
LocalManagementContextForTests.newInstance();
+        MapT<W> r2;
+        r2 = BeanWithTypeUtils.convertShallow(mgmt, MutableMap.of("map", 
MutableMap.of("a", ds)), new TypeToken<MapT<W>>() {}, true, null, true);
+        Asserts.assertEquals(r2.map.get("a").w.get(), "vv");
+
+        r2 = BeanWithTypeUtils.convertDeeply(mgmt, MutableMap.of("map", 
MutableMap.of("a", ds)), new TypeToken<MapT<W>>() {}, true, null, true);
+        Asserts.assertEquals(r2.map.get("a").w.get(), "vv");
+    }
+}
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 db6f362ef1..b28fbb2754 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
@@ -19,16 +19,11 @@
 package org.apache.brooklyn.core.resolve.jackson;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+
 import java.util.function.Supplier;
 import 
org.apache.brooklyn.core.resolve.jackson.WrappedValue.WrappedValuesInitialized;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
-import 
org.apache.brooklyn.util.core.flags.TypeCoercions.BrooklynCommonAdaptorTypeCoercions;
-import org.apache.brooklyn.util.core.task.BasicExecutionContext;
-import org.apache.brooklyn.util.core.task.BasicExecutionManager;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.javalang.JavaClassNames;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -152,8 +147,8 @@ public class WrappedValuesSerializationTest implements 
MapperTestFixture {
     }
 
     @Test
-    public void testDeserializeDsl() throws Exception {
-        // test in CAMP where DSL is registered
+    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);

Reply via email to