Repository: brooklyn-server
Updated Branches:
  refs/heads/master 92f1cb3ef -> 4f75b2185


Move jackson serialization configuration to core

Needs to be used by both rest-resource & software-base.


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/8aa2b75b
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/8aa2b75b
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/8aa2b75b

Branch: refs/heads/master
Commit: 8aa2b75b6f1ece763d1a3bf668950fa0861f4b69
Parents: 05283c8
Author: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com>
Authored: Fri Jun 17 18:55:45 2016 +0300
Committer: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com>
Committed: Mon Jun 20 15:50:22 2016 +0300

----------------------------------------------------------------------
 core/pom.xml                                    |  16 ++
 .../util/core/json/BidiSerialization.java       | 173 +++++++++++++++++++
 .../core/json/BrooklynObjectsJsonMapper.java    |  43 +++++
 .../json/ConfigurableSerializerProvider.java    |  91 ++++++++++
 .../ErrorAndToStringUnknownTypeSerializer.java  | 123 +++++++++++++
 .../util/core/json/MultimapSerializer.java      |  64 +++++++
 ...StrictPreferringFieldsVisibilityChecker.java | 108 ++++++++++++
 .../resources/AbstractBrooklynRestResource.java |   1 +
 .../brooklyn/rest/util/WebResourceUtils.java    |   1 -
 .../rest/util/json/BidiSerialization.java       | 173 -------------------
 .../util/json/BrooklynJacksonJsonProvider.java  |  21 +--
 .../json/ConfigurableSerializerProvider.java    |  91 ----------
 .../ErrorAndToStringUnknownTypeSerializer.java  | 123 -------------
 .../rest/util/json/MultimapSerializer.java      |  64 -------
 ...StrictPreferringFieldsVisibilityChecker.java | 108 ------------
 .../json/BrooklynJacksonSerializerTest.java     |   1 +
 16 files changed, 622 insertions(+), 579 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index ef3e850..c2be055 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -150,6 +150,22 @@
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.testng</groupId>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/core/src/main/java/org/apache/brooklyn/util/core/json/BidiSerialization.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/json/BidiSerialization.java 
b/core/src/main/java/org/apache/brooklyn/util/core/json/BidiSerialization.java
new file mode 100644
index 0000000..08f3bc1
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/json/BidiSerialization.java
@@ -0,0 +1,173 @@
+/*
+ * 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.util.core.json;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.objs.BrooklynObject;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class BidiSerialization {
+
+    protected final static ThreadLocal<Boolean> STRICT_SERIALIZATION = new 
ThreadLocal<Boolean>(); 
+
+    /**
+     * Sets strict serialization on, or off (the default), for the current 
thread.
+     * Recommended to be used in a <code>try { ... } finally { ... }</code> 
block
+     * with {@link #clearStrictSerialization()} at the end.
+     * <p>
+     * With strict serialization, classes must have public fields or annotated 
fields, else they will not be serialized.
+     */
+    public static void setStrictSerialization(Boolean value) {
+        STRICT_SERIALIZATION.set(value);
+    }
+
+    public static void clearStrictSerialization() {
+        STRICT_SERIALIZATION.remove();
+    }
+
+    public static boolean isStrictSerialization() {
+        Boolean result = STRICT_SERIALIZATION.get();
+        if (result!=null) return result;
+        return false;
+    }
+
+
+    public abstract static class AbstractWithManagementContextSerialization<T> 
{
+
+        protected class Serializer extends JsonSerializer<T> {
+            @Override
+            public void serialize(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+                
AbstractWithManagementContextSerialization.this.serialize(value, jgen, 
provider);
+            }
+        }
+        
+        protected class Deserializer extends JsonDeserializer<T> {
+            @Override
+            public T deserialize(JsonParser jp, DeserializationContext ctxt) 
throws IOException {
+                return 
AbstractWithManagementContextSerialization.this.deserialize(jp, ctxt);
+            }
+        }
+        
+        protected final Serializer serializer = new Serializer();
+        protected final Deserializer deserializer = new Deserializer();
+        protected final Class<T> type;
+        protected final ManagementContext mgmt;
+        
+        public AbstractWithManagementContextSerialization(Class<T> type, 
ManagementContext mgmt) {
+            this.type = type;
+            this.mgmt = mgmt;
+        }
+        
+        public JsonSerializer<T> getSerializer() {
+            return serializer;
+        }
+        
+        public JsonDeserializer<T> getDeserializer() {
+            return deserializer;
+        }
+
+        public void serialize(T value, JsonGenerator jgen, SerializerProvider 
provider) throws IOException {
+            jgen.writeStartObject();
+            writeBody(value, jgen, provider);
+            jgen.writeEndObject();
+        }
+
+        protected void writeBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+            jgen.writeStringField("type", value.getClass().getCanonicalName());
+            customWriteBody(value, jgen, provider);
+        }
+
+        public abstract void customWriteBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException;
+
+        public T deserialize(JsonParser jp, DeserializationContext ctxt) 
throws IOException {
+            @SuppressWarnings("unchecked")
+            Map<Object,Object> values = jp.readValueAs(Map.class);
+            String type = (String) values.get("type");
+            return customReadBody(type, values, jp, ctxt);
+        }
+
+        protected abstract T customReadBody(String type, Map<Object, Object> 
values, JsonParser jp, DeserializationContext ctxt) throws IOException;
+
+        public void install(SimpleModule module) {
+            module.addSerializer(type, serializer);
+            module.addDeserializer(type, deserializer);
+        }
+    }
+    
+    public static class ManagementContextSerialization extends 
AbstractWithManagementContextSerialization<ManagementContext> {
+        public ManagementContextSerialization(ManagementContext mgmt) { 
super(ManagementContext.class, mgmt); }
+        @Override
+        public void customWriteBody(ManagementContext value, JsonGenerator 
jgen, SerializerProvider provider) throws IOException {}
+        @Override
+        protected ManagementContext customReadBody(String type, Map<Object, 
Object> values, JsonParser jp, DeserializationContext ctxt) throws IOException {
+            return mgmt;
+        }
+    }
+    
+    public abstract static class AbstractBrooklynObjectSerialization<T extends 
BrooklynObject> extends AbstractWithManagementContextSerialization<T> {
+        public AbstractBrooklynObjectSerialization(Class<T> type, 
ManagementContext mgmt) { 
+            super(type, mgmt);
+        }
+        @Override
+        protected void writeBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+            jgen.writeStringField("type", type.getCanonicalName());
+            customWriteBody(value, jgen, provider);
+        }
+        @Override
+        public void customWriteBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+            jgen.writeStringField("id", value.getId());
+        }
+        @Override
+        protected T customReadBody(String type, Map<Object, Object> values, 
JsonParser jp, DeserializationContext ctxt) throws IOException {
+            return getInstanceFromId((String) values.get("id"));
+        }
+        protected abstract T getInstanceFromId(String id);
+    }
+
+    public static class EntitySerialization extends 
AbstractBrooklynObjectSerialization<Entity> {
+        public EntitySerialization(ManagementContext mgmt) { 
super(Entity.class, mgmt); }
+        @Override protected Entity getInstanceFromId(String id) { return 
mgmt.getEntityManager().getEntity(id); }
+    }
+    public static class LocationSerialization extends 
AbstractBrooklynObjectSerialization<Location> {
+        public LocationSerialization(ManagementContext mgmt) { 
super(Location.class, mgmt); }
+        @Override protected Location getInstanceFromId(String id) { return 
mgmt.getLocationManager().getLocation(id); }
+    }
+    // TODO how to look up policies and enrichers? (not essential...)
+//    public static class PolicySerialization extends 
AbstractBrooklynObjectSerialization<Policy> {
+//        public EntitySerialization(ManagementContext mgmt) { 
super(Policy.class, mgmt); }
+//        @Override protected Policy getKind(String id) { return 
mgmt.getEntityManager().getEntity(id); }
+//    }
+//    public static class EnricherSerialization extends 
AbstractBrooklynObjectSerialization<Enricher> {
+//        public EntitySerialization(ManagementContext mgmt) { 
super(Entity.class, mgmt); }
+//        @Override protected Enricher getKind(String id) { return 
mgmt.getEntityManager().getEntity(id); }
+//    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/core/src/main/java/org/apache/brooklyn/util/core/json/BrooklynObjectsJsonMapper.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/json/BrooklynObjectsJsonMapper.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/json/BrooklynObjectsJsonMapper.java
new file mode 100644
index 0000000..1a2972e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/json/BrooklynObjectsJsonMapper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed 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.util.core.json;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class BrooklynObjectsJsonMapper {
+    public static ObjectMapper newMapper(ManagementContext mgmt) {
+        ConfigurableSerializerProvider sp = new 
ConfigurableSerializerProvider();
+        sp.setUnknownTypeSerializer(new 
ErrorAndToStringUnknownTypeSerializer());
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setSerializerProvider(sp);
+        mapper.setVisibilityChecker(new 
PossiblyStrictPreferringFieldsVisibilityChecker());
+
+        SimpleModule mapperModule = new SimpleModule("Brooklyn", new 
Version(0, 0, 0, "ignored", null, null));
+
+        new 
BidiSerialization.ManagementContextSerialization(mgmt).install(mapperModule);
+        new BidiSerialization.EntitySerialization(mgmt).install(mapperModule);
+        new 
BidiSerialization.LocationSerialization(mgmt).install(mapperModule);
+
+        mapperModule.addSerializer(new MultimapSerializer());
+        mapper.registerModule(mapperModule);
+        return mapper;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/core/src/main/java/org/apache/brooklyn/util/core/json/ConfigurableSerializerProvider.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/json/ConfigurableSerializerProvider.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/json/ConfigurableSerializerProvider.java
new file mode 100644
index 0000000..055f289
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/json/ConfigurableSerializerProvider.java
@@ -0,0 +1,91 @@
+/*
+ * 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.util.core.json;
+
+import java.io.IOException;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonStreamContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializationConfig;
+import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
+import com.fasterxml.jackson.databind.ser.SerializerFactory;
+
+/** allows the serializer-of-last-resort to be customized, ie used for 
unknown-types */
+final class ConfigurableSerializerProvider extends DefaultSerializerProvider {
+
+    private static final long serialVersionUID = 6094990395562170217L;
+    protected JsonSerializer<Object> unknownTypeSerializer;
+
+    public ConfigurableSerializerProvider() {}
+
+    @Override
+    public DefaultSerializerProvider createInstance(SerializationConfig 
config, SerializerFactory jsf) {
+        return new ConfigurableSerializerProvider(config, this, jsf);
+    }
+
+    public ConfigurableSerializerProvider(SerializationConfig config, 
ConfigurableSerializerProvider src, SerializerFactory jsf) {
+        super(src, config, jsf);
+        unknownTypeSerializer = src.unknownTypeSerializer;
+    }
+
+    @Override
+    public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> 
unknownType) {
+        if (unknownTypeSerializer!=null) return unknownTypeSerializer;
+        return super.getUnknownTypeSerializer(unknownType);
+    }
+
+    public void setUnknownTypeSerializer(JsonSerializer<Object> 
unknownTypeSerializer) {
+        this.unknownTypeSerializer = unknownTypeSerializer;
+    }
+
+    @Override
+    public void serializeValue(JsonGenerator jgen, Object value) throws 
IOException {
+        JsonStreamContext ctxt = jgen.getOutputContext();
+        try {
+            super.serializeValue(jgen, value);
+        } catch (Exception e) {
+            onSerializationException(ctxt, jgen, value, e);
+        }
+    }
+
+    @Override
+    public void serializeValue(JsonGenerator jgen, Object value, JavaType 
rootType) throws IOException {
+        JsonStreamContext ctxt = jgen.getOutputContext();
+        try {
+            super.serializeValue(jgen, value, rootType);
+        } catch (Exception e) {
+            onSerializationException(ctxt, jgen, value, e);
+        }
+    }
+
+    protected void onSerializationException(JsonStreamContext ctxt, 
JsonGenerator jgen, Object value, Exception e) throws IOException {
+        Exceptions.propagateIfFatal(e);
+
+        JsonSerializer<Object> unknownTypeSerializer = 
getUnknownTypeSerializer(value.getClass());
+        if (unknownTypeSerializer instanceof 
ErrorAndToStringUnknownTypeSerializer) {
+            
((ErrorAndToStringUnknownTypeSerializer)unknownTypeSerializer).serializeFromError(ctxt,
 e, value, jgen, this);
+        } else {
+            unknownTypeSerializer.serialize(value, jgen, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/core/src/main/java/org/apache/brooklyn/util/core/json/ErrorAndToStringUnknownTypeSerializer.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/json/ErrorAndToStringUnknownTypeSerializer.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/json/ErrorAndToStringUnknownTypeSerializer.java
new file mode 100644
index 0000000..613c55f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/json/ErrorAndToStringUnknownTypeSerializer.java
@@ -0,0 +1,123 @@
+/*
+ * 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.util.core.json;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.javalang.Reflections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonStreamContext;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.impl.UnknownSerializer;
+
+/**
+ * for non-json-serializable classes (quite a lot of them!) simply provide a 
sensible error message and a toString.
+ * TODO maybe we want to attempt to serialize fields instead?  (but being 
careful not to be self-referential!)
+ */
+public class ErrorAndToStringUnknownTypeSerializer extends UnknownSerializer {
+
+    private static final Logger log = 
LoggerFactory.getLogger(ErrorAndToStringUnknownTypeSerializer.class);
+    private static Set<String> WARNED_CLASSES = 
Collections.synchronizedSet(MutableSet.<String>of());
+
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider 
provider) throws IOException {
+        if (BidiSerialization.isStrictSerialization())
+            throw new JsonMappingException("Cannot serialize object containing 
"+value.getClass().getName()+" when strict serialization requested");
+
+        serializeFromError(jgen.getOutputContext(), null, value, jgen, 
provider);
+    }
+
+    public void serializeFromError(JsonStreamContext ctxt, @Nullable Exception 
error, Object value, JsonGenerator jgen, SerializerProvider 
configurableSerializerProvider) throws IOException {
+        if (log.isDebugEnabled())
+            log.debug("Recovering from json serialization error, serializing 
"+value+": "+error);
+
+        if (BidiSerialization.isStrictSerialization())
+            throw new JsonMappingException("Cannot serialize "
+                + (ctxt!=null && !ctxt.inRoot() ? "object containing " : "")
+                + value.getClass().getName()+" when strict serialization 
requested");
+
+        if (WARNED_CLASSES.add(value.getClass().getCanonicalName())) {
+            log.warn("Standard serialization not possible for 
"+value.getClass()+" ("+value+")", error);
+        }
+        JsonStreamContext newCtxt = jgen.getOutputContext();
+
+        // very odd, but flush seems necessary when working with large 
objects; presumably a buffer which is allowed to clear itself?
+        // without this, when serializing the large (1.5M) Server json object 
from BrooklynJacksonSerializerTest creates invalid json,
+        // containing:  "foo":false,"{"error":true,...
+        jgen.flush();
+
+        boolean createObject = !newCtxt.inObject() || 
newCtxt.getCurrentName()!=null;
+        if (createObject) {
+            jgen.writeStartObject();
+        }
+
+        if (allowEmpty(value.getClass())) {
+            // write nothing
+        } else {
+
+            jgen.writeFieldName("error");
+            jgen.writeBoolean(true);
+
+            jgen.writeFieldName("errorType");
+            
jgen.writeString(NotSerializableException.class.getCanonicalName());
+
+            jgen.writeFieldName("type");
+            jgen.writeString(value.getClass().getCanonicalName());
+
+            jgen.writeFieldName("toString");
+            jgen.writeString(value.toString());
+
+            if (error!=null) {
+                jgen.writeFieldName("causedByError");
+                jgen.writeString(error.toString());
+            }
+
+        }
+
+        if (createObject) {
+            jgen.writeEndObject();
+        }
+
+        while (newCtxt!=null && !newCtxt.equals(ctxt)) {
+            if (jgen.getOutputContext().inArray()) { jgen.writeEndArray(); 
continue; }
+            if (jgen.getOutputContext().inObject()) { jgen.writeEndObject(); 
continue; }
+            break;
+        }
+
+    }
+
+    protected boolean allowEmpty(Class<? extends Object> clazz) {
+        if (clazz.getAnnotation(JsonSerialize.class)!=null && 
Reflections.hasNoNonObjectFields(clazz)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/core/src/main/java/org/apache/brooklyn/util/core/json/MultimapSerializer.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/json/MultimapSerializer.java 
b/core/src/main/java/org/apache/brooklyn/util/core/json/MultimapSerializer.java
new file mode 100644
index 0000000..d825e3e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/json/MultimapSerializer.java
@@ -0,0 +1,64 @@
+/*
+ * 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.util.core.json;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+/**
+ * Provides a serializer for {@link Multimap} instances.
+ * <p>
+ * When Brooklyn's Jackson dependency is updated from 
org.codehaus.jackson:1.9.13 to
+ * com.fasterxml.jackson:2.3+ then this class should be replaced with a 
dependency on
+ * jackson-datatype-guava and a GuavaModule registered with Brooklyn's 
ObjectMapper.
+ * Check the guava version when doing the switch as it could be incompatible 
with the
+ * version used by Brooklyn.
+ */
+@Beta
+public class MultimapSerializer extends StdSerializer<Multimap<?, ?>> {
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected MultimapSerializer() {
+        super((Class<Multimap<?, ?>>) (Class) Multimap.class);
+    }
+
+    @Override
+    public void serialize(Multimap<?, ?> value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+        jgen.writeStartObject();
+        writeEntries(value, jgen, provider);
+        jgen.writeEndObject();
+    }
+
+    private void writeEntries(Multimap<?, ?> value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+        for (Map.Entry<?, ? extends Collection<?>> entry : 
value.asMap().entrySet()) {
+            provider.findKeySerializer(provider.constructType(String.class), 
null)
+                    .serialize(entry.getKey(), jgen, provider);
+            
provider.defaultSerializeValue(Lists.newArrayList(entry.getValue()), jgen);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/core/src/main/java/org/apache/brooklyn/util/core/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
new file mode 100644
index 0000000..afe5a7f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
@@ -0,0 +1,108 @@
+/*
+ * 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.util.core.json;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
+
+import static com.fasterxml.jackson.annotation.JsonAutoDetect.*;
+
+/** a visibility checker which disables getters, but allows private access,
+ * unless {@link BidiSerialization#isStrictSerialization()} is enabled in 
which case public fields or annotations must be used.
+ * <p>
+ * the reason for this change to visibility
+ * is that getters might generate a copy, resulting in infinite loops, whereas 
field access should never do so.
+ * (see e.g. test in {@link BrooklynJacksonSerializerTest} which uses a 
sensor+config object whose getTypeToken
+ * causes infinite recursion)
+ **/
+public class PossiblyStrictPreferringFieldsVisibilityChecker implements 
VisibilityChecker<PossiblyStrictPreferringFieldsVisibilityChecker> {
+    VisibilityChecker<?>
+        vizDefault = new VisibilityChecker.Std(Visibility.NONE, 
Visibility.NONE, Visibility.NONE, Visibility.ANY, Visibility.ANY),
+        vizStrict = new VisibilityChecker.Std(Visibility.NONE, 
Visibility.NONE, Visibility.NONE, Visibility.PUBLIC_ONLY, 
Visibility.PUBLIC_ONLY);
+    
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
with(JsonAutoDetect ann) { throw new UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
with(Visibility v) { throw new UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withVisibility(PropertyAccessor method, Visibility v) { throw new 
UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withGetterVisibility(Visibility v) { throw new UnsupportedOperationException(); 
}
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withIsGetterVisibility(Visibility v) { throw new 
UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withSetterVisibility(Visibility v) { throw new UnsupportedOperationException(); 
}
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withCreatorVisibility(Visibility v) { throw new 
UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withFieldVisibility(Visibility v) { throw new UnsupportedOperationException(); }
+    
+    protected VisibilityChecker<?> viz() {
+        return BidiSerialization.isStrictSerialization() ? vizStrict : 
vizDefault;
+    }
+    
+    @Override public boolean isGetterVisible(Method m) {
+        return viz().isGetterVisible(m);
+    }
+
+    @Override
+    public boolean isGetterVisible(AnnotatedMethod m) {
+        return isGetterVisible(m.getAnnotated());
+    }
+
+    @Override
+    public boolean isIsGetterVisible(Method m) {
+        return viz().isIsGetterVisible(m);
+    }
+
+    @Override
+    public boolean isIsGetterVisible(AnnotatedMethod m) {
+        return isIsGetterVisible(m.getAnnotated());
+    }
+
+    @Override
+    public boolean isSetterVisible(Method m) {
+        return viz().isSetterVisible(m);
+    }
+
+    @Override
+    public boolean isSetterVisible(AnnotatedMethod m) {
+        return isSetterVisible(m.getAnnotated());
+    }
+
+    @Override
+    public boolean isCreatorVisible(Member m) {
+        return viz().isCreatorVisible(m);
+    }
+
+    @Override
+    public boolean isCreatorVisible(AnnotatedMember m) {
+        return isCreatorVisible(m.getMember());
+    }
+
+    @Override
+    public boolean isFieldVisible(Field f) {
+        return viz().isFieldVisible(f);
+    }
+
+    @Override
+    public boolean isFieldVisible(AnnotatedField f) {
+        return isFieldVisible(f.getAnnotated());
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
index adf50dd..2c47866 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
@@ -24,6 +24,7 @@ import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.ContextResolver;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.core.config.render.RendererHints;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
index f3723e6..5e8b7f9 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
@@ -32,7 +32,6 @@ import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.rest.domain.ApiError;
 import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
-import org.apache.brooklyn.util.core.osgi.Compat;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException;
 import org.apache.brooklyn.util.net.Urls;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java
deleted file mode 100644
index 93cae3f..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.rest.util.json;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.mgmt.ManagementContext;
-import org.apache.brooklyn.api.objs.BrooklynObject;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-public class BidiSerialization {
-
-    protected final static ThreadLocal<Boolean> STRICT_SERIALIZATION = new 
ThreadLocal<Boolean>(); 
-
-    /**
-     * Sets strict serialization on, or off (the default), for the current 
thread.
-     * Recommended to be used in a <code>try { ... } finally { ... }</code> 
block
-     * with {@link #clearStrictSerialization()} at the end.
-     * <p>
-     * With strict serialization, classes must have public fields or annotated 
fields, else they will not be serialized.
-     */
-    public static void setStrictSerialization(Boolean value) {
-        STRICT_SERIALIZATION.set(value);
-    }
-
-    public static void clearStrictSerialization() {
-        STRICT_SERIALIZATION.remove();
-    }
-
-    public static boolean isStrictSerialization() {
-        Boolean result = STRICT_SERIALIZATION.get();
-        if (result!=null) return result;
-        return false;
-    }
-
-
-    public abstract static class AbstractWithManagementContextSerialization<T> 
{
-
-        protected class Serializer extends JsonSerializer<T> {
-            @Override
-            public void serialize(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
-                
AbstractWithManagementContextSerialization.this.serialize(value, jgen, 
provider);
-            }
-        }
-        
-        protected class Deserializer extends JsonDeserializer<T> {
-            @Override
-            public T deserialize(JsonParser jp, DeserializationContext ctxt) 
throws IOException {
-                return 
AbstractWithManagementContextSerialization.this.deserialize(jp, ctxt);
-            }
-        }
-        
-        protected final Serializer serializer = new Serializer();
-        protected final Deserializer deserializer = new Deserializer();
-        protected final Class<T> type;
-        protected final ManagementContext mgmt;
-        
-        public AbstractWithManagementContextSerialization(Class<T> type, 
ManagementContext mgmt) {
-            this.type = type;
-            this.mgmt = mgmt;
-        }
-        
-        public JsonSerializer<T> getSerializer() {
-            return serializer;
-        }
-        
-        public JsonDeserializer<T> getDeserializer() {
-            return deserializer;
-        }
-
-        public void serialize(T value, JsonGenerator jgen, SerializerProvider 
provider) throws IOException {
-            jgen.writeStartObject();
-            writeBody(value, jgen, provider);
-            jgen.writeEndObject();
-        }
-
-        protected void writeBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
-            jgen.writeStringField("type", value.getClass().getCanonicalName());
-            customWriteBody(value, jgen, provider);
-        }
-
-        public abstract void customWriteBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException;
-
-        public T deserialize(JsonParser jp, DeserializationContext ctxt) 
throws IOException {
-            @SuppressWarnings("unchecked")
-            Map<Object,Object> values = jp.readValueAs(Map.class);
-            String type = (String) values.get("type");
-            return customReadBody(type, values, jp, ctxt);
-        }
-
-        protected abstract T customReadBody(String type, Map<Object, Object> 
values, JsonParser jp, DeserializationContext ctxt) throws IOException;
-
-        public void install(SimpleModule module) {
-            module.addSerializer(type, serializer);
-            module.addDeserializer(type, deserializer);
-        }
-    }
-    
-    public static class ManagementContextSerialization extends 
AbstractWithManagementContextSerialization<ManagementContext> {
-        public ManagementContextSerialization(ManagementContext mgmt) { 
super(ManagementContext.class, mgmt); }
-        @Override
-        public void customWriteBody(ManagementContext value, JsonGenerator 
jgen, SerializerProvider provider) throws IOException {}
-        @Override
-        protected ManagementContext customReadBody(String type, Map<Object, 
Object> values, JsonParser jp, DeserializationContext ctxt) throws IOException {
-            return mgmt;
-        }
-    }
-    
-    public abstract static class AbstractBrooklynObjectSerialization<T extends 
BrooklynObject> extends AbstractWithManagementContextSerialization<T> {
-        public AbstractBrooklynObjectSerialization(Class<T> type, 
ManagementContext mgmt) { 
-            super(type, mgmt);
-        }
-        @Override
-        protected void writeBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
-            jgen.writeStringField("type", type.getCanonicalName());
-            customWriteBody(value, jgen, provider);
-        }
-        @Override
-        public void customWriteBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
-            jgen.writeStringField("id", value.getId());
-        }
-        @Override
-        protected T customReadBody(String type, Map<Object, Object> values, 
JsonParser jp, DeserializationContext ctxt) throws IOException {
-            return getInstanceFromId((String) values.get("id"));
-        }
-        protected abstract T getInstanceFromId(String id);
-    }
-
-    public static class EntitySerialization extends 
AbstractBrooklynObjectSerialization<Entity> {
-        public EntitySerialization(ManagementContext mgmt) { 
super(Entity.class, mgmt); }
-        @Override protected Entity getInstanceFromId(String id) { return 
mgmt.getEntityManager().getEntity(id); }
-    }
-    public static class LocationSerialization extends 
AbstractBrooklynObjectSerialization<Location> {
-        public LocationSerialization(ManagementContext mgmt) { 
super(Location.class, mgmt); }
-        @Override protected Location getInstanceFromId(String id) { return 
mgmt.getLocationManager().getLocation(id); }
-    }
-    // TODO how to look up policies and enrichers? (not essential...)
-//    public static class PolicySerialization extends 
AbstractBrooklynObjectSerialization<Policy> {
-//        public EntitySerialization(ManagementContext mgmt) { 
super(Policy.class, mgmt); }
-//        @Override protected Policy getKind(String id) { return 
mgmt.getEntityManager().getEntity(id); }
-//    }
-//    public static class EnricherSerialization extends 
AbstractBrooklynObjectSerialization<Enricher> {
-//        public EntitySerialization(ManagementContext mgmt) { 
super(Entity.class, mgmt); }
-//        @Override protected Enricher getKind(String id) { return 
mgmt.getEntityManager().getEntity(id); }
-//    }
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
index 83106ab..c099cf2 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
@@ -34,12 +34,11 @@ import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
 import org.apache.brooklyn.core.server.BrooklynServiceAttributes;
 import org.apache.brooklyn.rest.util.OsgiCompat;
+import org.apache.brooklyn.util.core.json.BrooklynObjectsJsonMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
 
 @Provider
@@ -153,23 +152,7 @@ public class BrooklynJacksonJsonProvider extends 
JacksonJsonProvider implements
             throw new IllegalStateException("No management context available 
for creating ObjectMapper");
         }
 
-        ConfigurableSerializerProvider sp = new 
ConfigurableSerializerProvider();
-        sp.setUnknownTypeSerializer(new 
ErrorAndToStringUnknownTypeSerializer());
-
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.setSerializerProvider(sp);
-        mapper.setVisibilityChecker(new 
PossiblyStrictPreferringFieldsVisibilityChecker());
-
-        SimpleModule mapperModule = new SimpleModule("Brooklyn", new 
Version(0, 0, 0, "ignored", null, null));
-
-        new 
BidiSerialization.ManagementContextSerialization(mgmt).install(mapperModule);
-        new BidiSerialization.EntitySerialization(mgmt).install(mapperModule);
-        new 
BidiSerialization.LocationSerialization(mgmt).install(mapperModule);
-
-        mapperModule.addSerializer(new MultimapSerializer());
-        mapper.registerModule(mapperModule);
-
-        return mapper;
+        return BrooklynObjectsJsonMapper.newMapper(mgmt);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
deleted file mode 100644
index b77202f..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.rest.util.json;
-
-import java.io.IOException;
-
-import org.apache.brooklyn.util.exceptions.Exceptions;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonStreamContext;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializationConfig;
-import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
-import com.fasterxml.jackson.databind.ser.SerializerFactory;
-
-/** allows the serializer-of-last-resort to be customized, ie used for 
unknown-types */
-final class ConfigurableSerializerProvider extends DefaultSerializerProvider {
-
-    private static final long serialVersionUID = 6094990395562170217L;
-    protected JsonSerializer<Object> unknownTypeSerializer;
-
-    public ConfigurableSerializerProvider() {}
-
-    @Override
-    public DefaultSerializerProvider createInstance(SerializationConfig 
config, SerializerFactory jsf) {
-        return new ConfigurableSerializerProvider(config, this, jsf);
-    }
-
-    public ConfigurableSerializerProvider(SerializationConfig config, 
ConfigurableSerializerProvider src, SerializerFactory jsf) {
-        super(src, config, jsf);
-        unknownTypeSerializer = src.unknownTypeSerializer;
-    }
-
-    @Override
-    public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> 
unknownType) {
-        if (unknownTypeSerializer!=null) return unknownTypeSerializer;
-        return super.getUnknownTypeSerializer(unknownType);
-    }
-
-    public void setUnknownTypeSerializer(JsonSerializer<Object> 
unknownTypeSerializer) {
-        this.unknownTypeSerializer = unknownTypeSerializer;
-    }
-
-    @Override
-    public void serializeValue(JsonGenerator jgen, Object value) throws 
IOException {
-        JsonStreamContext ctxt = jgen.getOutputContext();
-        try {
-            super.serializeValue(jgen, value);
-        } catch (Exception e) {
-            onSerializationException(ctxt, jgen, value, e);
-        }
-    }
-
-    @Override
-    public void serializeValue(JsonGenerator jgen, Object value, JavaType 
rootType) throws IOException {
-        JsonStreamContext ctxt = jgen.getOutputContext();
-        try {
-            super.serializeValue(jgen, value, rootType);
-        } catch (Exception e) {
-            onSerializationException(ctxt, jgen, value, e);
-        }
-    }
-
-    protected void onSerializationException(JsonStreamContext ctxt, 
JsonGenerator jgen, Object value, Exception e) throws IOException {
-        Exceptions.propagateIfFatal(e);
-
-        JsonSerializer<Object> unknownTypeSerializer = 
getUnknownTypeSerializer(value.getClass());
-        if (unknownTypeSerializer instanceof 
ErrorAndToStringUnknownTypeSerializer) {
-            
((ErrorAndToStringUnknownTypeSerializer)unknownTypeSerializer).serializeFromError(ctxt,
 e, value, jgen, this);
-        } else {
-            unknownTypeSerializer.serialize(value, jgen, this);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
deleted file mode 100644
index e529ec9..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.rest.util.json;
-
-import java.io.IOException;
-import java.io.NotSerializableException;
-import java.util.Collections;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.util.collections.MutableSet;
-import org.apache.brooklyn.util.javalang.Reflections;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonStreamContext;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.ser.impl.UnknownSerializer;
-
-/**
- * for non-json-serializable classes (quite a lot of them!) simply provide a 
sensible error message and a toString.
- * TODO maybe we want to attempt to serialize fields instead?  (but being 
careful not to be self-referential!)
- */
-public class ErrorAndToStringUnknownTypeSerializer extends UnknownSerializer {
-
-    private static final Logger log = 
LoggerFactory.getLogger(ErrorAndToStringUnknownTypeSerializer.class);
-    private static Set<String> WARNED_CLASSES = 
Collections.synchronizedSet(MutableSet.<String>of());
-
-    @Override
-    public void serialize(Object value, JsonGenerator jgen, SerializerProvider 
provider) throws IOException {
-        if (BidiSerialization.isStrictSerialization())
-            throw new JsonMappingException("Cannot serialize object containing 
"+value.getClass().getName()+" when strict serialization requested");
-
-        serializeFromError(jgen.getOutputContext(), null, value, jgen, 
provider);
-    }
-
-    public void serializeFromError(JsonStreamContext ctxt, @Nullable Exception 
error, Object value, JsonGenerator jgen, SerializerProvider 
configurableSerializerProvider) throws IOException {
-        if (log.isDebugEnabled())
-            log.debug("Recovering from json serialization error, serializing 
"+value+": "+error);
-
-        if (BidiSerialization.isStrictSerialization())
-            throw new JsonMappingException("Cannot serialize "
-                + (ctxt!=null && !ctxt.inRoot() ? "object containing " : "")
-                + value.getClass().getName()+" when strict serialization 
requested");
-
-        if (WARNED_CLASSES.add(value.getClass().getCanonicalName())) {
-            log.warn("Standard serialization not possible for 
"+value.getClass()+" ("+value+")", error);
-        }
-        JsonStreamContext newCtxt = jgen.getOutputContext();
-
-        // very odd, but flush seems necessary when working with large 
objects; presumably a buffer which is allowed to clear itself?
-        // without this, when serializing the large (1.5M) Server json object 
from BrooklynJacksonSerializerTest creates invalid json,
-        // containing:  "foo":false,"{"error":true,...
-        jgen.flush();
-
-        boolean createObject = !newCtxt.inObject() || 
newCtxt.getCurrentName()!=null;
-        if (createObject) {
-            jgen.writeStartObject();
-        }
-
-        if (allowEmpty(value.getClass())) {
-            // write nothing
-        } else {
-
-            jgen.writeFieldName("error");
-            jgen.writeBoolean(true);
-
-            jgen.writeFieldName("errorType");
-            
jgen.writeString(NotSerializableException.class.getCanonicalName());
-
-            jgen.writeFieldName("type");
-            jgen.writeString(value.getClass().getCanonicalName());
-
-            jgen.writeFieldName("toString");
-            jgen.writeString(value.toString());
-
-            if (error!=null) {
-                jgen.writeFieldName("causedByError");
-                jgen.writeString(error.toString());
-            }
-
-        }
-
-        if (createObject) {
-            jgen.writeEndObject();
-        }
-
-        while (newCtxt!=null && !newCtxt.equals(ctxt)) {
-            if (jgen.getOutputContext().inArray()) { jgen.writeEndArray(); 
continue; }
-            if (jgen.getOutputContext().inObject()) { jgen.writeEndObject(); 
continue; }
-            break;
-        }
-
-    }
-
-    protected boolean allowEmpty(Class<? extends Object> clazz) {
-        if (clazz.getAnnotation(JsonSerialize.class)!=null && 
Reflections.hasNoNonObjectFields(clazz)) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
deleted file mode 100644
index f750e86..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.rest.util.json;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-
-/**
- * Provides a serializer for {@link Multimap} instances.
- * <p>
- * When Brooklyn's Jackson dependency is updated from 
org.codehaus.jackson:1.9.13 to
- * com.fasterxml.jackson:2.3+ then this class should be replaced with a 
dependency on
- * jackson-datatype-guava and a GuavaModule registered with Brooklyn's 
ObjectMapper.
- * Check the guava version when doing the switch as it could be incompatible 
with the
- * version used by Brooklyn.
- */
-@Beta
-public class MultimapSerializer extends StdSerializer<Multimap<?, ?>> {
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected MultimapSerializer() {
-        super((Class<Multimap<?, ?>>) (Class) Multimap.class);
-    }
-
-    @Override
-    public void serialize(Multimap<?, ?> value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
-        jgen.writeStartObject();
-        writeEntries(value, jgen, provider);
-        jgen.writeEndObject();
-    }
-
-    private void writeEntries(Multimap<?, ?> value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
-        for (Map.Entry<?, ? extends Collection<?>> entry : 
value.asMap().entrySet()) {
-            provider.findKeySerializer(provider.constructType(String.class), 
null)
-                    .serialize(entry.getKey(), jgen, provider);
-            
provider.defaultSerializeValue(Lists.newArrayList(entry.getValue()), jgen);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
deleted file mode 100644
index e474467..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.rest.util.json;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
-import com.fasterxml.jackson.databind.introspect.AnnotatedField;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
-import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
-
-import static com.fasterxml.jackson.annotation.JsonAutoDetect.*;
-
-/** a visibility checker which disables getters, but allows private access,
- * unless {@link BidiSerialization#isStrictSerialization()} is enabled in 
which case public fields or annotations must be used.
- * <p>
- * the reason for this change to visibility
- * is that getters might generate a copy, resulting in infinite loops, whereas 
field access should never do so.
- * (see e.g. test in {@link BrooklynJacksonSerializerTest} which uses a 
sensor+config object whose getTypeToken
- * causes infinite recursion)
- **/
-public class PossiblyStrictPreferringFieldsVisibilityChecker implements 
VisibilityChecker<PossiblyStrictPreferringFieldsVisibilityChecker> {
-    VisibilityChecker<?>
-        vizDefault = new VisibilityChecker.Std(Visibility.NONE, 
Visibility.NONE, Visibility.NONE, Visibility.ANY, Visibility.ANY),
-        vizStrict = new VisibilityChecker.Std(Visibility.NONE, 
Visibility.NONE, Visibility.NONE, Visibility.PUBLIC_ONLY, 
Visibility.PUBLIC_ONLY);
-    
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
with(JsonAutoDetect ann) { throw new UnsupportedOperationException(); }
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
with(Visibility v) { throw new UnsupportedOperationException(); }
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withVisibility(PropertyAccessor method, Visibility v) { throw new 
UnsupportedOperationException(); }
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withGetterVisibility(Visibility v) { throw new UnsupportedOperationException(); 
}
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withIsGetterVisibility(Visibility v) { throw new 
UnsupportedOperationException(); }
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withSetterVisibility(Visibility v) { throw new UnsupportedOperationException(); 
}
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withCreatorVisibility(Visibility v) { throw new 
UnsupportedOperationException(); }
-    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withFieldVisibility(Visibility v) { throw new UnsupportedOperationException(); }
-    
-    protected VisibilityChecker<?> viz() {
-        return BidiSerialization.isStrictSerialization() ? vizStrict : 
vizDefault;
-    }
-    
-    @Override public boolean isGetterVisible(Method m) { 
-        return viz().isGetterVisible(m);
-    }
-
-    @Override
-    public boolean isGetterVisible(AnnotatedMethod m) {
-        return isGetterVisible(m.getAnnotated());
-    }
-
-    @Override
-    public boolean isIsGetterVisible(Method m) {
-        return viz().isIsGetterVisible(m);
-    }
-
-    @Override
-    public boolean isIsGetterVisible(AnnotatedMethod m) {
-        return isIsGetterVisible(m.getAnnotated());
-    }
-
-    @Override
-    public boolean isSetterVisible(Method m) {
-        return viz().isSetterVisible(m);
-    }
-
-    @Override
-    public boolean isSetterVisible(AnnotatedMethod m) {
-        return isSetterVisible(m.getAnnotated());
-    }
-
-    @Override
-    public boolean isCreatorVisible(Member m) {
-        return viz().isCreatorVisible(m);
-    }
-
-    @Override
-    public boolean isCreatorVisible(AnnotatedMember m) {
-        return isCreatorVisible(m.getMember());
-    }
-
-    @Override
-    public boolean isFieldVisible(Field f) {
-        return viz().isFieldVisible(f);
-    }
-
-    @Override
-    public boolean isFieldVisible(AnnotatedField f) {
-        return isFieldVisible(f.getAnnotated());
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8aa2b75b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
index 2dd5ae1..eb91f0a 100644
--- 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
+++ 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.json.BidiSerialization;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.Strings;

Reply via email to