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
The following commit(s) were added to refs/heads/master by this push:
new f1a7df85da more cautious convertShallow object references
f1a7df85da is described below
commit f1a7df85da1dc128ea5c118dcbc836219ad5a1ba
Author: Alex Heneveld <[email protected]>
AuthorDate: Tue May 30 00:09:20 2023 +0100
more cautious convertShallow object references
fix errors where in some weird cases the reference could be accepted as the
value (as a string),
due to failure of jackson to preserve aliases through token buffer
---
.../jackson/ObjectReferencingSerialization.java | 56 +++++++++++++---------
.../BrooklynMiscJacksonSerializationTest.java | 41 +++++++++++++---
2 files changed, 69 insertions(+), 28 deletions(-)
diff --git
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
index 219e110178..e703f3a868 100644
---
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
+++
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
@@ -41,8 +41,11 @@ import com.google.common.reflect.TypeToken;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Type;
+import java.util.function.Supplier;
+
import
org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier;
import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.text.Identifiers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,6 +57,8 @@ public class ObjectReferencingSerialization {
private static final Logger LOG =
LoggerFactory.getLogger(ObjectReferencingSerialization.class);
+ public static final String PREFIX =
"_"+ObjectReferencingSerialization.class.getSimpleName()+"_";
+
final BiMap<String,Object> backingMap = HashBiMap.create();
ObjectMapper mapper = null;
@@ -99,7 +104,7 @@ public class ObjectReferencingSerialization {
public void serialize(Object value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
String id = backingMap.inverse().get(value);
if (id==null) {
- id = Identifiers.makeRandomId(12);
+ id = PREFIX+Identifiers.makeRandomId(12);
backingMap.put(id, value);
}
@@ -146,30 +151,37 @@ public class ObjectReferencingSerialization {
protected Object deserializeWrapper(JsonParser jp,
DeserializationContext ctxt, BiFunctionThrowsIoException<JsonParser,
DeserializationContext, Object> nestedDeserialize) throws IOException {
String v = jp.getCurrentToken()== JsonToken.VALUE_STRING ?
jp.getValueAsString() : null;
if (v!=null) {
- Type expected = _valueType!=null ? _valueType : _valueClass;
-
- // not sure if we ever need to look at contextual type
- Type expected2 = ctxt.getContextualType()==null ? null :
ctxt.getContextualType();
- if (expected2!=null) {
- if (expected==null) {
- expected = expected2;
- } else {
- // we have two expectations
- LOG.debug("Object reference deserialization ambiguity,
expected "+expected+" and "+expected2);
+ // we need to exclude the case where a literal 'v' was
expected;
+ // TokenBuffer does not preserve YAMLParser.isCurrentAlias()
so we can't use that;
+ // instead we use a prefix to ensure our references are almost
certainly unique
+ if (v.startsWith(PREFIX)) {
+ Type expected = _valueType != null ? _valueType :
_valueClass;
+
+ // not sure if we ever need to look at contextual type
+ Type expected2 = ctxt.getContextualType() == null ? null :
ctxt.getContextualType();
+ if (expected2 != null) {
+ if (expected == null) {
+ expected = expected2;
+ } else {
+ // we have two expectations
+ LOG.debug("Object reference deserialization
ambiguity, expected " + expected + " and " + expected2);
+ }
+ }
+ if (expected == null) {
+ expected = Object.class;
}
- }
- if (expected==null) {
- expected = Object.class;
- }
- Object result = backingMap.get(v);
- if (result!=null) {
- // Because of how
UntypedObjectDeserializer.deserializeWithType treats strings
- // we cannot trust string being expected (if we could, we
could exclude backing map lookup!)
- if (!String.class.equals(expected)) {
- result = TypeCoercions.coerce(result,
TypeToken.of(expected));
+ Object result = backingMap.get(v);
+ if (result != null) {
+ // previously we excluded a string from being
expected, since we wouldn't have converted that to a reference;
+ // however there are contexts where a string is
expected but other types are _accepted_ so just trust it if we come here,
+ // and if the expected type is overly strict, return
the original object.
+ // if the token buffer is used too much we might have
lost the alias reference and still end up with a string,
+ // but so long as this deserializer is preferred which
it normally is, losing the alias reference is okay.
+ return ((Maybe)TypeCoercions.tryCoerce(result,
TypeToken.of(expected))).or(result);
+ } else {
+ LOG.debug("Odd - what looks like a reference was
received but not found: "+v);
}
- return result;
}
}
return nestedDeserialize.apply(jp, ctxt);
diff --git
a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java
b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java
index c25b3730b8..4e74a964ee 100644
---
a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java
+++
b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java
@@ -27,12 +27,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.util.*;
-import java.util.function.BiConsumer;
-
import com.google.common.reflect.TypeToken;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
@@ -40,6 +34,7 @@ import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
import org.apache.brooklyn.util.core.units.ByteSize;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.javalang.JavaClassNames;
@@ -52,6 +47,11 @@ import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.*;
+import java.util.function.BiConsumer;
+
public class BrooklynMiscJacksonSerializationTest implements MapperTestFixture
{
private static final Logger LOG =
LoggerFactory.getLogger(BrooklynMiscJacksonSerializationTest.class);
@@ -126,6 +126,35 @@ public class BrooklynMiscJacksonSerializationTest
implements MapperTestFixture {
Asserts.assertTrue(f1==f2, "different instances for "+f1+" and "+f2);
}
+ public static class ObjRefAcceptingStringSource {
+ String src;
+ ObjRefAcceptingStringSource bar;
+
+ public ObjRefAcceptingStringSource() {}
+ public ObjRefAcceptingStringSource(String src) {
+ this.src = src;
+ }
+ }
+
+ @Test
+ public void testConvertShallowDoesntAcceptIdsForStrings() throws
JsonProcessingException {
+ ManagementContext mgmt = LocalManagementContextForTests.newInstance();
+
+ Asserts.assertFailsWith(() -> {
+ ObjRefAcceptingStringSource b =
BeanWithTypeUtils.convertShallow(mgmt, MutableMap.of("bar", new
DeferredSupplier() {
+ @Override
+ public Object get() {
+ return "xxx";
+ }
+ }), TypeToken.of(ObjRefAcceptingStringSource.class), false,
null, true);
+ // ensure the ID of a serialized object isn't treated as a
reference
+ Asserts.fail("Should have failed, instead got: " + b.bar.src);
+ return b;
+ }, e -> Asserts.expectedFailureContains(e, "Problem deserializing
property 'bar'"));
+
+ ObjRefAcceptingStringSource b = BeanWithTypeUtils.convertShallow(mgmt,
MutableMap.of("bar", new ObjRefAcceptingStringSource("good")),
TypeToken.of(ObjRefAcceptingStringSource.class), false, null, true);
+ Asserts.assertEquals(b.bar.src, "good");
+ }
@Test
public void testDurationCustomSerialization() throws Exception {