add primitive-or-object deserializer, fix bug resolving entity exprs in triggers
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
index 7519a37..fb1b100 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
@@ -18,6 +18,16 @@
*/
package org.apache.brooklyn.camp.brooklyn;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -38,7 +48,13 @@
import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils;
import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.core.entity.*;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.entity.Dumper;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAdjuncts;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.sensor.Sensors;
@@ -48,7 +64,12 @@
import org.apache.brooklyn.core.typereg.BasicTypeImplementationPlan;
import org.apache.brooklyn.core.typereg.JavaClassNameTypePlanTransformer;
import org.apache.brooklyn.core.typereg.RegisteredTypes;
-import org.apache.brooklyn.core.workflow.*;
+import org.apache.brooklyn.core.workflow.WorkflowBasicTest;
+import org.apache.brooklyn.core.workflow.WorkflowEffector;
+import org.apache.brooklyn.core.workflow.WorkflowExecutionContext;
+import org.apache.brooklyn.core.workflow.WorkflowPolicy;
+import org.apache.brooklyn.core.workflow.WorkflowStepDefinition;
+import org.apache.brooklyn.core.workflow.WorkflowStepInstanceExecutionContext;
import org.apache.brooklyn.core.workflow.steps.flow.LogWorkflowStep;
import org.apache.brooklyn.core.workflow.store.WorkflowStatePersistenceViaSensors;
import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
@@ -74,16 +95,6 @@
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-import java.time.temporal.TemporalUnit;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecContains;
import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecsContain;
import static org.testng.Assert.assertTrue;
@@ -291,6 +302,17 @@
doTestWorkflowPolicy("condition: { sensor: not_exist }\n" + "period: 200 ms", null);
}
+ @Test
+ public void testWorkflowPolicyTriggersWithEntityId() throws Exception {
+ doTestWorkflowPolicy("triggers: [ { sensor: theTrigger, entity: other_entity } ]", Duration.seconds(1)::isLongerThan, null, true);
+ }
+ @Test
+ public void testWorkflowPolicyTriggersWithEntityInstance() throws Exception {
+ // see org.apache.brooklyn.core.resolve.jackson.BrooklynMiscJacksonSerializationTest.testPrimitiveWithObjectForEntity
+ doTestWorkflowPolicy("triggers: [ { sensor: theTrigger, entity: $brooklyn:entity(\"other_entity\") } ]", Duration.seconds(1)::isLongerThan, null, true);
+ }
+
+
static final AttributeSensor<Object> MY_WORKFLOW_SENSOR = Sensors.newSensor(Object.class, "myWorkflowSensor");
Entity doTestWorkflowSensor(String triggers, Predicate<Duration> timeCheckOrNullIfShouldFail) throws Exception {
@@ -350,9 +372,13 @@
}
public void doTestWorkflowPolicy(String triggers, Predicate<Duration> timeCheckOrNullIfShouldFail, Consumer<Policy> extraChecks) throws Exception {
+ doTestWorkflowPolicy(triggers, timeCheckOrNullIfShouldFail, extraChecks, false);
+ }
+ public void doTestWorkflowPolicy(String triggers, Predicate<Duration> timeCheckOrNullIfShouldFail, Consumer<Policy> extraChecks, boolean useOtherEntity) throws Exception {
Entity app = createAndStartApplication(
"services:",
"- type: " + BasicEntity.class.getName(),
+ " id: main_entity",
" brooklyn.policies:",
" - type: workflow-policy",
" brooklyn.config:",
@@ -370,13 +396,17 @@
" v: ${v}",
" - transform map x = ${out} | yaml",
" - set-sensor myWorkflowSensor = ${x}",
+ "- type: " + BasicEntity.class.getName(),
+ " id: other_entity",
"");
Stopwatch sw = Stopwatch.createStarted();
waitForApplicationTasks(app);
Duration d1 = Duration.of(sw);
- Entity entity = Iterables.getOnlyElement(app.getChildren());
+ Iterator<Entity> ci = app.getChildren().iterator();
+ Entity entity = ci.next();
+ Entity otherEntity = ci.next();
Policy policy = entity.policies().asList().stream().filter(p -> p instanceof WorkflowPolicy).findAny().get();
Asserts.assertEquals(policy.getDisplayName(), "Set myWorkflowSensor");
// should really ID be settable from flag?
@@ -390,7 +420,7 @@
Asserts.assertThat(d2, Duration.millis(500)::isLongerThan);
// EntityAsserts.assertAttributeEqualsEventually(entity, s, MutableMap.of("foo", "bar", "v", 0));
- entity.sensors().set(Sensors.newStringSensor("theTrigger"), "go");
+ (useOtherEntity ? otherEntity : entity).sensors().set(Sensors.newStringSensor("theTrigger"), "go");
EntityAsserts.assertAttributeEqualsEventually(MutableMap.of("timeout", "5s"), entity, MY_WORKFLOW_SENSOR, MutableMap.of("foo", "bar", "v", 0));
// EntityAsserts.assertAttributeEqualsEventually(entity, s, MutableMap.of("foo", "bar", "v", 1));
Duration d3 = Duration.of(sw).subtract(d2);
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java
index 6824985..b266cd2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java
@@ -267,7 +267,9 @@
boolean canTryWithoutType = !typeIdFindResult.isUnambiguous;
try {
Object result = _deserializeTypedForId(p, ctxt, tb, typeIdFindResult.type);
- if (_idResolver instanceof HasBaseType) {
+ if (result==null) {
+ LOG.trace("Null result deserializing");
+ } else if (_idResolver instanceof HasBaseType) {
JavaType baseType = ((HasBaseType) _idResolver).getBaseType();
if (baseType != null) {
Class<?> rawClass = baseType.getRawClass();
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java
index fb5e553..f294ee8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java
@@ -31,15 +31,21 @@
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.google.common.reflect.TypeToken;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.EntityManager;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.api.objs.BrooklynObject;
import org.apache.brooklyn.api.objs.BrooklynObjectType;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.mgmt.internal.EntityManagerInternal;
+import org.apache.brooklyn.core.mgmt.internal.NonDeploymentManagementContext;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.core.flags.BrooklynTypeNameResolution;
import org.apache.brooklyn.util.core.flags.FlagUtils;
import org.apache.brooklyn.util.core.predicates.DslPredicates;
+import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.javalang.Boxing;
import org.apache.brooklyn.util.time.Duration;
@@ -356,6 +362,17 @@
// we could support 'current' to use tasks to resolve, which might be handy
BrooklynObject result = mgmt.lookup(value);
if (result!=null) return result;
+
+ Entity currentEntity = BrooklynTaskTags.getContextEntity(Tasks.current());
+ if (currentEntity!=null) {
+ // during initialization, we can look relative to ourselves, since entities aren't available in mgmt.lookup
+ Iterable<Entity> ents = ((EntityManagerInternal) mgmt.getEntityManager()).getAllEntitiesInApplication(currentEntity.getApplication());
+ for (Entity e : ents) {
+ if (Objects.equals(value, e.getId())) {
+ return e;
+ }
+ }
+ }
throw new IllegalStateException("Entity or other BrooklynObject '"+value+"' is not known here");
}
@@ -443,7 +460,11 @@
return super.convertStringToObject(value, p, ctxt);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
- LOG.warn("Reference to BrooklynObject "+value+" which is unknown or no longer available; replacing with 'null'");
+ if (BrooklynObjectSerialization.this.mgmt instanceof NonDeploymentManagementContext) {
+ LOG.warn("Reference to BrooklynObject " + value + " which is unknown or not yet known, using NonDeployment context; replacing with 'null': "+e);
+ } else {
+ LOG.warn("Reference to BrooklynObject " + value + " which is unknown or no longer available; replacing with 'null': "+e);
+ }
return null;
}
}
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java
index 93d8b7f..5b2c5ff 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java
@@ -29,6 +29,8 @@
import java.io.IOException;
import java.util.Set;
import java.util.function.Function;
+
+import org.apache.brooklyn.util.core.units.Range;
import org.apache.brooklyn.util.core.xstream.ImmutableSetConverter;
import static org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.createBeanDeserializer;
@@ -152,5 +154,4 @@
/** try to do low level build so we don't recreate ourselves and loop endlessly */ true,
true);
}
-
}
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/PrimitiveTokenOrExpectedObject.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/PrimitiveTokenOrExpectedObject.java
new file mode 100644
index 0000000..ff342c2
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/PrimitiveTokenOrExpectedObject.java
@@ -0,0 +1,109 @@
+/*
+ * 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 java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+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.JsonMappingException;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.apache.brooklyn.core.resolve.jackson.PrimitiveTokenOrExpectedObject.PrimitiveTokenOrExpectedObjectDeserializer;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+
+import static org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.createBeanDeserializer;
+
+/**
+ * This is an object intended for use in Jackson setters. When supplied with a primitive token, the value is stored as the primitive.
+ * For anything else, it is deserialized according to the type T.
+ * Thus setters can inspect the object and call the right method, and a custom string can be handled specially without need for lower-level Jackson interrogation,
+ * but if the object, or a map of the object is supplied, it is used.
+ */
+@JsonDeserialize(using = PrimitiveTokenOrExpectedObjectDeserializer.class)
+public class PrimitiveTokenOrExpectedObject<T> {
+
+ // exactly one of these will be set
+ public T object;
+ public Object primitive;
+
+ public boolean hasObject() { return object!=null; }
+ public boolean hasPrimitive() { return primitive!=null; }
+ public boolean hasStringPrimitive() { return primitive instanceof String; }
+
+ public T asObject() { return object; }
+ public Object asPrimitive() { return primitive; }
+ public String asString() { if (hasStringPrimitive()) return (String)primitive; return null; }
+
+ public static class PrimitiveTokenOrExpectedObjectDeserializer extends JsonSymbolDependentDeserializer {
+ public PrimitiveTokenOrExpectedObjectDeserializer() {
+ super();
+ }
+ @Override
+ public JavaType getDefaultType() {
+ return ctxt.constructType(Object.class);
+ }
+
+ protected Maybe<Object> getTokenValue(JsonToken token, JsonParser p) {
+ try {
+ if (SIMPLE_TOKENS.contains(token)) {
+ if (JsonToken.VALUE_STRING.equals(token)) return Maybe.of(p.getValueAsString());
+ if (JsonToken.VALUE_NUMBER_INT.equals(token)) return Maybe.of(p.getValueAsInt());
+ if (JsonToken.VALUE_NUMBER_FLOAT.equals(token)) return Maybe.of(p.getValueAsDouble());
+ if (token.isBoolean()) return Maybe.of(p.getValueAsBoolean());
+ if (JsonToken.VALUE_NULL.equals(token)) return Maybe.ofAllowingNull(null);
+ }
+ return Maybe.absent();
+ } catch (IOException e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+ @Override
+ protected Object deserializeToken(JsonParser p) throws IOException {
+ PrimitiveTokenOrExpectedObject result = new PrimitiveTokenOrExpectedObject();
+ result.primitive = getTokenValue(p.getCurrentToken(), p).get();
+ return result;
+ }
+
+ @Override
+ protected Object deserializeObject(JsonParser p) throws IOException {
+ PrimitiveTokenOrExpectedObject result = new PrimitiveTokenOrExpectedObject();
+ result.object = super.deserializeObject(p);
+ return result;
+ }
+
+ protected JsonDeserializer<?> getObjectDeserializer() throws IOException, JsonProcessingException {
+ if (type!=null && PrimitiveTokenOrExpectedObject.class.equals(type.getRawClass())) {
+ // this should always happen
+ return ctxt.findRootValueDeserializer(type.containedType(0));
+ } else {
+ return super.getObjectDeserializer();
+ }
+ }
+
+ public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
+ return super.createContextual(ctxt, property);
+ }
+ }
+}
diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/AbstractAddTriggerableSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/AbstractAddTriggerableSensor.java
index f9adc8a..a5c2c5a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/sensor/AbstractAddTriggerableSensor.java
+++ b/core/src/main/java/org/apache/brooklyn/core/sensor/AbstractAddTriggerableSensor.java
@@ -26,6 +26,7 @@
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonSetter;
import com.google.common.annotations.Beta;
import com.google.common.base.Predicates;
import com.google.common.reflect.TypeToken;
@@ -38,6 +39,7 @@
import org.apache.brooklyn.core.entity.EntityPredicates;
import org.apache.brooklyn.core.feed.PollConfig;
import org.apache.brooklyn.core.mgmt.internal.AppGroupTraverser;
+import org.apache.brooklyn.core.resolve.jackson.PrimitiveTokenOrExpectedObject;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.predicates.DslPredicates;
@@ -105,26 +107,28 @@
t = new SensorFeedTrigger();
t.sensorName = (String) ti;
} else {
- throw new IllegalStateException("Trigger should be a map specifyin entity and sensor");
+ throw new IllegalStateException("Trigger should be a map specifying entity and sensor");
}
}
Entity entity = t.entity;
- if (entity==null && t.entityId!=null) {
- String desiredComponentId = t.entityId;
- List<Entity> firstGroupOfMatches = AppGroupTraverser.findFirstGroupOfMatches(context, true,
- Predicates.and(EntityPredicates.configEqualTo(BrooklynConfigKeys.PLAN_ID, desiredComponentId), x->true)::apply);
- if (firstGroupOfMatches.isEmpty()) {
- firstGroupOfMatches = AppGroupTraverser.findFirstGroupOfMatches(context, true,
- Predicates.and(EntityPredicates.idEqualTo(desiredComponentId), x->true)::apply);
- }
- if (!firstGroupOfMatches.isEmpty()) {
- entity = firstGroupOfMatches.get(0);
+ if (entity==null) {
+ if (t.entityId != null) {
+ String desiredComponentId = t.entityId;
+ List<Entity> firstGroupOfMatches = AppGroupTraverser.findFirstGroupOfMatches(context, true,
+ Predicates.and(EntityPredicates.configEqualTo(BrooklynConfigKeys.PLAN_ID, desiredComponentId), x -> true)::apply);
+ if (firstGroupOfMatches.isEmpty()) {
+ firstGroupOfMatches = AppGroupTraverser.findFirstGroupOfMatches(context, true,
+ Predicates.and(EntityPredicates.idEqualTo(desiredComponentId), x -> true)::apply);
+ }
+ if (!firstGroupOfMatches.isEmpty()) {
+ entity = firstGroupOfMatches.get(0);
+ } else {
+ throw new IllegalStateException("Cannot find entity with ID '" + desiredComponentId + "'");
+ }
} else {
- throw new IllegalStateException("Cannot find entity with ID '"+desiredComponentId+"'");
+ entity = context;
}
- } else {
- entity = context;
}
Sensor sensor = t.sensor;
@@ -157,6 +161,17 @@
// could support predicates on the value; but we do it on the entity which is enough
+ @JsonSetter
+// @JsonDeserialize(using = PrimitiveOrObjectDeserializer.class)
+// @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+ public void setEntity(PrimitiveTokenOrExpectedObject<Entity> po) {
+ //@JsonTypeInfo(use = JsonTypeInfo.Id.NONE) @JsonDeserialize(using = PrimitiveOrObjectDeserializer.class)
+// Object entity) {
+ if (po.hasObject()) setEntity(po.asObject());
+ else if (po.hasStringPrimitive()) setEntity(po.asString());
+ else if (entity==null) { /* do nothing */ }
+ else throw new IllegalArgumentException("Invalid input for entity to "+this+": "+entity);
+ }
public void setEntity(Entity entity) {
this.entity = entity;
}
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 4e74a96..fb38bb9 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
@@ -18,6 +18,15 @@
*/
package org.apache.brooklyn.core.resolve.jackson;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.function.BiConsumer;
+
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -28,8 +37,12 @@
import com.fasterxml.jackson.databind.util.TokenBuffer;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
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.mgmt.ManagementContext;
+import org.apache.brooklyn.core.sensor.AbstractAddTriggerableSensor.SensorFeedTrigger;
import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.stock.BasicApplicationImpl;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
@@ -47,11 +60,6 @@
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);
@@ -59,7 +67,7 @@
private ObjectMapper mapper;
public ObjectMapper mapper() {
- if (mapper==null) mapper = BeanWithTypeUtils.newMapper(null, false, null, true);
+ if (mapper == null) mapper = BeanWithTypeUtils.newMapper(null, false, null, true);
return mapper;
}
@@ -70,12 +78,13 @@
// baseline
- static class EmptyObject {}
+ static class EmptyObject {
+ }
@Test
public void testMapperDoesntBreakBasicThings() throws Exception {
Asserts.assertEquals(deser("\"hello\""), "hello");
- Asserts.assertInstanceOf(deser("{\"type\":\""+EmptyObject.class.getName()+"\"}"), EmptyObject.class);
+ Asserts.assertInstanceOf(deser("{\"type\":\"" + EmptyObject.class.getName() + "\"}"), EmptyObject.class);
}
@Test
@@ -90,7 +99,7 @@
public String toString() {
return "Obj{" +
"foo='" + foo + '\'' +
- "}@"+ System.identityHashCode(this);
+ "}@" + System.identityHashCode(this);
}
}
@@ -100,37 +109,42 @@
mapper = BeanWithTypeUtils.applyCommonMapperConfig(mapper, null, false, null, true);
mapper = new ObjectReferencingSerialization().useAndApplytoMapper(mapper);
- ObjForSerializingAsReference f1 = new ObjForSerializingAsReference(); f1.foo = "1";
- ObjForSerializingAsReference f2 = new ObjForSerializingAsReference(); f2.foo = "2";
+ ObjForSerializingAsReference f1 = new ObjForSerializingAsReference();
+ f1.foo = "1";
+ ObjForSerializingAsReference f2 = new ObjForSerializingAsReference();
+ f2.foo = "2";
String out = ser(MutableMap.of("a", f1, "b", f2, "c", f1));
- LOG.info("Result of "+ JavaClassNames.niceClassAndMethod()+": "+out);
+ LOG.info("Result of " + JavaClassNames.niceClassAndMethod() + ": " + out);
Map in = deser(out,
Map.class
// new TypeToken<Map<String, ObjForSerializingAsReference>>() {}
);
- ObjForSerializingAsReference a = (ObjForSerializingAsReference)in.get("a");
- ObjForSerializingAsReference b = (ObjForSerializingAsReference)in.get("b");
- ObjForSerializingAsReference c = (ObjForSerializingAsReference)in.get("c");
- Asserts.assertTrue(a.foo.equals(c.foo), "expected same foo value for a and c - "+a+" != "+c);
+ ObjForSerializingAsReference a = (ObjForSerializingAsReference) in.get("a");
+ ObjForSerializingAsReference b = (ObjForSerializingAsReference) in.get("b");
+ ObjForSerializingAsReference c = (ObjForSerializingAsReference) in.get("c");
+ Asserts.assertTrue(a.foo.equals(c.foo), "expected same foo value for a and c - " + a + " != " + c);
Asserts.assertTrue(!b.foo.equals(c.foo), "expected different foo value for a and b");
- Asserts.assertTrue(a == c, "expected same instance for a and c - "+a+" != "+c);
+ Asserts.assertTrue(a == c, "expected same instance for a and c - " + a + " != " + c);
Asserts.assertTrue(a != b, "expected different instance for a and b");
}
@Test
public void testObjectReferences() throws IOException {
- ObjForSerializingAsReference f1 = new ObjForSerializingAsReference(); f1.foo = "1";
+ ObjForSerializingAsReference f1 = new ObjForSerializingAsReference();
+ f1.foo = "1";
Object f2 = new ObjectReferencingSerialization().serializeAndDeserialize(f1);
Asserts.assertEquals(f1, f2);
- Asserts.assertTrue(f1==f2, "different instances for "+f1+" and "+f2);
+ Asserts.assertTrue(f1 == f2, "different instances for " + f1 + " and " + f2);
}
public static class ObjRefAcceptingStringSource {
String src;
ObjRefAcceptingStringSource bar;
- public ObjRefAcceptingStringSource() {}
+ public ObjRefAcceptingStringSource() {
+ }
+
public ObjRefAcceptingStringSource(String src) {
this.src = src;
}
@@ -141,16 +155,16 @@
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 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");
@@ -185,7 +199,7 @@
public static class DateTimeBean {
String x;
Date juDate;
-// LocalDateTime localDateTime;
+ // LocalDateTime localDateTime;
GregorianCalendar calendar;
Instant instant;
}
@@ -196,11 +210,11 @@
// mapper.findAndRegisterModules();
DateTimeBean impl = new DateTimeBean();
- Asserts.assertEquals(ser(impl, DateTimeBean.class), "{}" );
+ Asserts.assertEquals(ser(impl, DateTimeBean.class), "{}");
impl.x = "foo";
- impl.juDate = new Date(60*1000);
+ impl.juDate = new Date(60 * 1000);
// impl.localDateTime = LocalDateTime.of(2020, 1, 1, 12, 0, 0, 0);
impl.calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.ROOT);
impl.calendar.set(2020, 0, 1, 12, 0, 0);
@@ -222,18 +236,18 @@
"instant: 2020-01-01T12:00:00Z",
""
), DateTimeBean.class);
- Assert.assertEquals( impl2.x, impl.x );
- Assert.assertEquals( impl2.juDate, impl.juDate );
+ Assert.assertEquals(impl2.x, impl.x);
+ Assert.assertEquals(impl2.juDate, impl.juDate);
// Assert.assertEquals( impl2.localDateTime, impl.localDateTime );
// Assert.assertEquals( impl2.calendar, impl.calendar );
- Assert.assertEquals( impl2.instant, impl.instant );
+ Assert.assertEquals(impl2.instant, impl.instant);
}
@Test
public void testInstantConversionFromVarious() throws Exception {
mapper = BeanWithTypeUtils.newYamlMapper(null, false, null, true);
long utc = new Date().getTime();
- Instant inst = mapper.readerFor(Instant.class).readValue( mapper.writeValueAsString(utc) );
+ Instant inst = mapper.readerFor(Instant.class).readValue(mapper.writeValueAsString(utc));
// below known not to work, as long is converted to ["j...Long", utc] which we don't process
//mapper.readerFor(Instant.class).readValue( mapper.writerFor(Object.class).writeValueAsString(utc) );
@@ -242,8 +256,8 @@
BeanWithTypeUtils.convertShallow(mgmt, utc, TypeToken.of(Instant.class), false, null, false);
BeanWithTypeUtils.convertDeeply(mgmt, utc, TypeToken.of(Instant.class), false, null, false);
- BeanWithTypeUtils.convertShallow(mgmt, ""+utc, TypeToken.of(Instant.class), false, null, false);
- BeanWithTypeUtils.convertDeeply(mgmt, ""+utc, TypeToken.of(Instant.class), false, null, false);
+ BeanWithTypeUtils.convertShallow(mgmt, "" + utc, TypeToken.of(Instant.class), false, null, false);
+ BeanWithTypeUtils.convertDeeply(mgmt, "" + utc, TypeToken.of(Instant.class), false, null, false);
BeanWithTypeUtils.convertShallow(mgmt, inst, TypeToken.of(Instant.class), false, null, false);
BeanWithTypeUtils.convertDeeply(mgmt, inst, TypeToken.of(Instant.class), false, null, false);
@@ -270,7 +284,7 @@
public void testStringBean() throws Exception {
Duration d = mapper().readValue("\"1m\"", Duration.class);
Asserts.assertEquals(d, Duration.ONE_MINUTE);
- Object d0 = mapper().readValue("{\"type\":\""+Duration.class.getName()+"\",\"value\":\"1s\"}", Object.class);
+ Object d0 = mapper().readValue("{\"type\":\"" + Duration.class.getName() + "\",\"value\":\"1s\"}", Object.class);
Asserts.assertEquals(d0, Duration.ONE_SECOND);
}
@@ -282,7 +296,7 @@
@Test
public void testJsonPassThrough() throws Exception {
- BiConsumer<String,Object> check = (input, expected) -> {
+ BiConsumer<String, Object> check = (input, expected) -> {
try {
JsonPassThroughDeserializer.JsonObjectHolder x = mapper().readValue(input, JsonPassThroughDeserializer.JsonObjectHolder.class);
Asserts.assertEquals(x.value, expected);
@@ -297,7 +311,7 @@
check.accept("42", 42);
check.accept("{\"k\":\"v\"}", MutableMap.of("k", "v"));
check.accept("[\"a\",1]", MutableList.of("a", 1));
- check.accept("[\"a\",{\"type\":\""+Duration.class.getName()+"\",\"value\":\"1s\"}]",
+ check.accept("[\"a\",{\"type\":\"" + Duration.class.getName() + "\",\"value\":\"1s\"}]",
MutableList.of("a", MutableMap.of("type", Duration.class.getName(), "value", "1s")));
}
@@ -311,11 +325,17 @@
public Object subtypeWanted;
public String x;
}
+
@JsonDeserialize(using = JsonDeserializer.None.class)
- static class SampleFromStringSubtype extends SampleFromStringDeserialized {}
+ static class SampleFromStringSubtype extends SampleFromStringDeserialized {
+ }
+
@JsonDeserialize(using = JsonDeserializer.None.class)
- static class SampleFromStringSubtype1 extends SampleFromStringSubtype {}
- static class SampleFromStringSubtype2 extends SampleFromStringSubtype {}
+ static class SampleFromStringSubtype1 extends SampleFromStringSubtype {
+ }
+
+ static class SampleFromStringSubtype2 extends SampleFromStringSubtype {
+ }
static class SampleFromStringDeserializer extends JsonDeserializer {
@Override
@@ -326,13 +346,13 @@
ctxt);
Integer rawi = null;
- if (raw instanceof String) rawi = Integer.parseInt((String)raw);
- if (raw instanceof Integer) rawi = (Integer)raw;
- if (rawi!=null) {
+ if (raw instanceof String) rawi = Integer.parseInt((String) raw);
+ if (raw instanceof Integer) rawi = (Integer) raw;
+ if (rawi != null) {
SampleFromStringSubtype result = null;
- if (rawi==1) result = new SampleFromStringSubtype1();
- if (rawi==2) result = new SampleFromStringSubtype2();
- if (result!=null) {
+ if (rawi == 1) result = new SampleFromStringSubtype1();
+ if (rawi == 2) result = new SampleFromStringSubtype2();
+ if (result != null) {
result.subtypeWanted = raw;
}
return result;
@@ -340,9 +360,9 @@
if (raw instanceof Map) {
Integer stw = TypeCoercions.tryCoerce(((Map) raw).get("subtypeWanted"), Integer.class).orNull();
- if (stw!=null && stw>=0) {
+ if (stw != null && stw >= 0) {
try {
- return ctxt.findNonContextualValueDeserializer(ctxt.constructType(Class.forName(SampleFromStringSubtype.class.getName()+stw))).deserialize(
+ return ctxt.findNonContextualValueDeserializer(ctxt.constructType(Class.forName(SampleFromStringSubtype.class.getName() + stw))).deserialize(
BrooklynJacksonSerializationUtils.createParserFromTokenBufferAndParser(buffer, p), ctxt);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
@@ -361,25 +381,41 @@
mapper = BeanWithTypeUtils.newSimpleYamlMapper(); //YAMLMapper.builder().build();
s = mapper.readerFor(SampleFromStringDeserialized.class).readValue("1");
Asserts.assertInstanceOf(s, SampleFromStringSubtype1.class);
- Asserts.assertEquals( ((SampleFromStringSubtype)s).subtypeWanted, 1 );
+ Asserts.assertEquals(((SampleFromStringSubtype) s).subtypeWanted, 1);
s = mapper.readerFor(SampleFromStringDeserialized.class).readValue("\"2\"");
Asserts.assertInstanceOf(s, SampleFromStringSubtype2.class);
- Asserts.assertEquals( ((SampleFromStringSubtype)s).subtypeWanted, "2" );
+ Asserts.assertEquals(((SampleFromStringSubtype) s).subtypeWanted, "2");
s = mapper.readerFor(SampleFromStringDeserialized.class).readValue("subtypeWanted: 1");
Asserts.assertInstanceOf(s, SampleFromStringSubtype1.class);
- Asserts.assertEquals( ((SampleFromStringSubtype)s).subtypeWanted, 1 );
+ Asserts.assertEquals(((SampleFromStringSubtype) s).subtypeWanted, 1);
s = mapper.readerFor(SampleFromStringDeserialized.class).readValue("subtypeWanted: \"-1\"");
Asserts.assertEquals(s.getClass(), SampleFromStringSubtype.class);
- Asserts.assertEquals( ((SampleFromStringSubtype)s).subtypeWanted, "-1" );
+ Asserts.assertEquals(((SampleFromStringSubtype) s).subtypeWanted, "-1");
s = TypeCoercions.coerce("1", SampleFromStringDeserialized.class);
Asserts.assertInstanceOf(s, SampleFromStringSubtype1.class);
- Asserts.assertEquals( ((SampleFromStringSubtype)s).subtypeWanted, "1" );
+ Asserts.assertEquals(((SampleFromStringSubtype) s).subtypeWanted, "1");
s = TypeCoercions.coerce(1, SampleFromStringDeserialized.class);
- Asserts.assertEquals( ((SampleFromStringSubtype)s).subtypeWanted, 1 );
+ Asserts.assertEquals(((SampleFromStringSubtype) s).subtypeWanted, 1);
}
-}
+ @Test
+ public void testPrimitiveWithObjectForEntity() throws Exception {
+ ManagementContext mgmt = LocalManagementContextForTests.newInstance();
+ Entity app = mgmt.getEntityManager().createEntity(EntitySpec.create(BasicApplicationImpl.class));
+
+ Asserts.assertThat(BeanWithTypeUtils.convert(mgmt,
+ MutableMap.of("entity", app, "sensor", "aTrigger"),
+ TypeToken.of(SensorFeedTrigger.class), true, null, true),
+ f -> f != null && app.equals(f.getEntity()) && f.getSensor().equals("aTrigger"));
+
+ Asserts.assertThat(BeanWithTypeUtils.convert(mgmt,
+ MutableMap.of("entity", app.getApplicationId(), "sensor", "aTrigger"),
+ TypeToken.of(SensorFeedTrigger.class), true, null, true),
+ f -> f != null && app.getId().equals(f.getEntity()) && f.getSensor().equals("aTrigger"));
+ }
+
+}
\ No newline at end of file