You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by ma...@apache.org on 2017/11/07 01:15:51 UTC

atlas git commit: ATLAS-2251: notification module updates (#2)

Repository: atlas
Updated Branches:
  refs/heads/ATLAS-2251 64e739da7 -> 48daa7cb5


ATLAS-2251: notification module updates (#2)


Project: http://git-wip-us.apache.org/repos/asf/atlas/repo
Commit: http://git-wip-us.apache.org/repos/asf/atlas/commit/48daa7cb
Tree: http://git-wip-us.apache.org/repos/asf/atlas/tree/48daa7cb
Diff: http://git-wip-us.apache.org/repos/asf/atlas/diff/48daa7cb

Branch: refs/heads/ATLAS-2251
Commit: 48daa7cb5cc15154f08764a3d294fa76f6f7cf21
Parents: 64e739d
Author: Madhan Neethiraj <ma...@apache.org>
Authored: Mon Nov 6 17:15:27 2017 -0800
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Mon Nov 6 17:15:40 2017 -0800

----------------------------------------------------------------------
 .../java/org/apache/atlas/type/AtlasType.java   | 109 ++++++-------------
 .../model/instance/AtlasSystemAttributes.java   |  12 ++
 .../org/apache/atlas/v1/model/instance/Id.java  |  86 ++++++++++++++-
 .../atlas/v1/model/instance/Referenceable.java  |  28 ++++-
 .../apache/atlas/v1/model/instance/Struct.java  |  55 +++++++++-
 .../model/notification/EntityNotification.java  |  13 +++
 .../v1/model/notification/HookNotification.java |  42 ++++---
 .../atlas/v1/model/typedef/Multiplicity.java    |  56 +++++++++-
 .../java/org/apache/atlas/hook/AtlasHook.java   |  12 +-
 .../notification/hook/HookNotificationTest.java |   2 -
 10 files changed, 300 insertions(+), 115 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/type/AtlasType.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasType.java b/intg/src/main/java/org/apache/atlas/type/AtlasType.java
index 1b09b93..62b1df5 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasType.java
@@ -28,7 +28,6 @@ import org.apache.atlas.v1.model.notification.HookNotification.EntityUpdateReque
 import org.apache.atlas.v1.model.notification.HookNotification.HookNotificationMessage;
 import org.apache.atlas.v1.model.notification.HookNotification.HookNotificationType;
 import org.apache.atlas.v1.model.notification.HookNotification.TypeRequest;
-import org.apache.atlas.v1.model.typedef.Multiplicity;
 import org.codehaus.jackson.*;
 import org.codehaus.jackson.map.*;
 import org.codehaus.jackson.map.module.SimpleModule;
@@ -58,8 +57,6 @@ public abstract class AtlasType {
 
         atlasSerDeModule.addSerializer(Date.class, new DateSerializer());
         atlasSerDeModule.addDeserializer(Date.class, new DateDeserializer());
-        atlasSerDeModule.addSerializer(Multiplicity.class, new MultiplicitySerializer());
-        atlasSerDeModule.addDeserializer(Multiplicity.class, new MultiplicityDeserializer());
         atlasSerDeModule.addDeserializer(HookNotificationMessage.class, new HookMessageDeserializer());
 
         mapperV1.registerModule(atlasSerDeModule);
@@ -209,83 +206,43 @@ public abstract class AtlasType {
         }
     }
 
-    static class MultiplicitySerializer extends JsonSerializer<Multiplicity> {
-        @Override
-        public void serialize(Multiplicity value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
-            if (value != null) {
-                if (value.equals(Multiplicity.REQUIRED)) {
-                    jgen.writeString("required");
-                } else if (value.equals(Multiplicity.OPTIONAL)) {
-                    jgen.writeString("optional");
-                } else if (value.equals(Multiplicity.COLLECTION)) {
-                    jgen.writeString("collection");
-                } else if (value.equals(Multiplicity.SET)) {
-                    jgen.writeString("set");
-                }
-            }
-        }
-    }
-
-    static class MultiplicityDeserializer extends JsonDeserializer<Multiplicity> {
-        @Override
-        public Multiplicity deserialize(JsonParser parser, DeserializationContext context) throws IOException {
-            Multiplicity ret = null;
-
-            String value = parser.readValueAs(String.class);
-
-            if (value != null) {
-                if (value.equals("required")) {
-                    ret = new Multiplicity(Multiplicity.REQUIRED);
-                } else if (value.equals("optional")) {
-                    ret = new Multiplicity(Multiplicity.OPTIONAL);
-                } else if (value.equals("collection")) {
-                    ret = new Multiplicity(Multiplicity.COLLECTION);
-                } else if (value.equals("set")) {
-                    ret = new Multiplicity(Multiplicity.SET);
-                }
-            }
-
-            if (ret == null) {
-                ret = new Multiplicity();
-            }
-
-            return ret;
-        }
-    }
-
     static class HookMessageDeserializer extends JsonDeserializer<HookNotificationMessage> {
         @Override
         public HookNotificationMessage deserialize(JsonParser parser, DeserializationContext context) throws IOException {
-            HookNotificationMessage ret = null;
-
-            ObjectMapper mapper = (ObjectMapper) parser.getCodec();
-            ObjectNode   root   = (ObjectNode) mapper.readTree(parser);
-
-            JsonNode             typeNode         = root.get("type");
-            String               strType          = typeNode.asText();
-            HookNotificationType notificationType = HookNotificationType.valueOf(strType);
-
-            switch (notificationType) {
-                case TYPE_CREATE:
-                case TYPE_UPDATE:
-                    ret = mapper.readValue(root, TypeRequest.class);
-                break;
-
-                case ENTITY_CREATE:
-                    ret = mapper.readValue(root, EntityCreateRequest.class);
-                    break;
-
-                case ENTITY_PARTIAL_UPDATE:
-                    ret = mapper.readValue(root, EntityPartialUpdateRequest.class);
-                    break;
-
-                case ENTITY_FULL_UPDATE:
-                    ret = mapper.readValue(root, EntityUpdateRequest.class);
-                    break;
+            HookNotificationMessage ret              = null;
+            ObjectMapper            mapper           = (ObjectMapper) parser.getCodec();
+            ObjectNode              root             = (ObjectNode) mapper.readTree(parser);
+            JsonNode                typeNode         = root != null ? root.get("type") : null;
+            String                  strType          = typeNode != null ? typeNode.asText() : null;
+            HookNotificationType    notificationType = strType != null ? HookNotificationType.valueOf(strType) : null;
+
+            if (notificationType != null) {
+                switch (notificationType) {
+                    case TYPE_CREATE:
+                    case TYPE_UPDATE:
+                        ret = mapper.readValue(root, TypeRequest.class);
+                        break;
+
+                    case ENTITY_CREATE:
+                        ret = mapper.readValue(root, EntityCreateRequest.class);
+                        break;
+
+                    case ENTITY_PARTIAL_UPDATE:
+                        ret = mapper.readValue(root, EntityPartialUpdateRequest.class);
+                        break;
+
+                    case ENTITY_FULL_UPDATE:
+                        ret = mapper.readValue(root, EntityUpdateRequest.class);
+                        break;
+
+                    case ENTITY_DELETE:
+                        ret = mapper.readValue(root, EntityDeleteRequest.class);
+                        break;
+                }
 
-                case ENTITY_DELETE:
-                    ret = mapper.readValue(root, EntityDeleteRequest.class);
-                    break;
+                if (ret != null) {
+                    ret.normalize();
+                }
             }
 
             return ret;

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/v1/model/instance/AtlasSystemAttributes.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/v1/model/instance/AtlasSystemAttributes.java b/intg/src/main/java/org/apache/atlas/v1/model/instance/AtlasSystemAttributes.java
index fba22cc..30d500d 100644
--- a/intg/src/main/java/org/apache/atlas/v1/model/instance/AtlasSystemAttributes.java
+++ b/intg/src/main/java/org/apache/atlas/v1/model/instance/AtlasSystemAttributes.java
@@ -27,6 +27,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 import java.io.Serializable;
 import java.util.Date;
+import java.util.Map;
 import java.util.Objects;
 
 import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
@@ -66,6 +67,17 @@ public class AtlasSystemAttributes implements Serializable {
         this.modifiedTime = modifiedTime;
     }
 
+    public AtlasSystemAttributes(Map<String, Object> map) {
+        this();
+
+        if (map != null) {
+            this.createdBy    = Id.asString(map.get("createdBy"));
+            this.modifiedBy   = Id.asString(map.get("modifiedBy"));
+            this.createdTime  = Id.asDate(map.get("createdTime"));
+            this.modifiedTime = Id.asDate(map.get("modifiedTime"));
+        }
+    }
+
     public String getCreatedBy(){
         return createdBy;
     }

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/v1/model/instance/Id.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/v1/model/instance/Id.java b/intg/src/main/java/org/apache/atlas/v1/model/instance/Id.java
index cd2951e..67e647d 100644
--- a/intg/src/main/java/org/apache/atlas/v1/model/instance/Id.java
+++ b/intg/src/main/java/org/apache/atlas/v1/model/instance/Id.java
@@ -19,6 +19,7 @@
 package org.apache.atlas.v1.model.instance;
 
 
+import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
@@ -28,6 +29,10 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 import java.io.Serializable;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -46,6 +51,8 @@ public class Id implements Serializable {
     @JsonIgnore
     private static AtomicLong s_nextId = new AtomicLong(System.nanoTime());
 
+    public static final String JSON_CLASS_ID = "org.apache.atlas.typesystem.json.InstanceSerialization$_Id";
+
     public enum EntityState { ACTIVE, DELETED }
 
     private String      id;
@@ -89,9 +96,20 @@ public class Id implements Serializable {
         this.state    = state == null ? EntityState.ACTIVE : EntityState.valueOf(state.toUpperCase());
     }
 
+    public Id(Map<String, Object> map) {
+        this();
+
+        if (map != null) {
+            this.id       = Id.asString(map.get("id"));
+            this.typeName = Id.asString(map.get("typeName"));
+            this.version  = Id.asInt(map.get("id"));
+            this.state    = Id.asEntityState(map.get("state"));
+        }
+    }
+
     // for serialization backward compatibility
     public String getJsonClass() {
-        return "org.apache.atlas.typesystem.json.InstanceSerialization$_Id";
+        return JSON_CLASS_ID;
     }
 
     public String getId() {
@@ -158,10 +176,10 @@ public class Id implements Serializable {
 
     @Override
     public String toString() {
-        return toString(new StringBuilder()).toString();
+        return asString(new StringBuilder()).toString();
     }
 
-    public StringBuilder toString(StringBuilder sb) {
+    public StringBuilder asString(StringBuilder sb) {
         if (sb == null) {
             sb = new StringBuilder();
         }
@@ -187,4 +205,66 @@ public class Id implements Serializable {
 
         return ret;
     }
+
+    static String asString(Object val) {
+        return val == null ? null : val.toString();
+    }
+
+    static int asInt(Object val) {
+        if (val != null) {
+            if (val instanceof Number) {
+                return ((Number)val).intValue();
+            }
+
+            try {
+                return Integer.parseInt(val.toString());
+            } catch (NumberFormatException excp) {
+                // ignore
+            }
+        }
+
+        return 0;
+    }
+
+    static Date asDate(Object val) {
+        if (val != null) {
+            if (val instanceof Date) {
+                return (Date) val;
+            } else if (val instanceof Number) {
+                return new Date(((Number)val).longValue());
+            }
+
+            try {
+                return AtlasBaseTypeDef.DATE_FORMATTER.parse(val.toString());
+            } catch (ParseException excp) {
+                // ignore
+            }
+        }
+
+        return null;
+    }
+
+    static EntityState asEntityState(Object val) {
+        if (val != null) {
+            if (val instanceof EntityState) {
+                return (EntityState) val;
+            }
+
+            try {
+                return EntityState.valueOf(val.toString());
+            } catch (Exception excp) {
+                // ignore
+            }
+        }
+
+        return EntityState.ACTIVE;
+    }
+
+    static Map asMap(Object val) {
+        return (val instanceof Map) ? ((Map) val) : null;
+    }
+
+    static List asList(Object val) {
+        return (val instanceof List) ? ((List) val) : null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/v1/model/instance/Referenceable.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/v1/model/instance/Referenceable.java b/intg/src/main/java/org/apache/atlas/v1/model/instance/Referenceable.java
index 158da45..8fc0acb 100644
--- a/intg/src/main/java/org/apache/atlas/v1/model/instance/Referenceable.java
+++ b/intg/src/main/java/org/apache/atlas/v1/model/instance/Referenceable.java
@@ -21,9 +21,11 @@ package org.apache.atlas.v1.model.instance;
 
 
 import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
+import org.apache.commons.collections.MapUtils;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonFilter;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
 import javax.xml.bind.annotation.XmlAccessType;
@@ -48,6 +50,8 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONL
 public class Referenceable extends Struct implements Serializable {
     private static final long serialVersionUID = 1L;
 
+    public static final String JSON_CLASS_REFERENCE = "org.apache.atlas.typesystem.json.InstanceSerialization$_Reference";
+
     private Id                    id;
     private Map<String, Struct>   traits     = new HashMap<>();
     private List<String>          traitNames = new ArrayList<>();
@@ -129,10 +133,30 @@ public class Referenceable extends Struct implements Serializable {
         }
     }
 
+    public Referenceable(Map<String, Object> map) {
+        super(map);
+
+        if (map != null) {
+            this.id               = new Id((Map)map.get("id"));
+            this.traitNames       = Id.asList(map.get("traitNames"));
+            this.systemAttributes = new AtlasSystemAttributes((Map) map.get("systemAttributes"));
+
+            Map traits = Id.asMap(map.get("traits"));
+
+            if (MapUtils.isNotEmpty(traits)) {
+                this.traits = new HashMap<>(traits.size());
+
+                for (Object key : traits.keySet()) {
+                    this.traits.put(Id.asString(key), new Struct(Id.asMap(traits.get(key))));
+                }
+            }
+        }
+    }
+
 
     // for serialization backward compatibility
     public String getJsonClass() {
-        return "org.apache.atlas.typesystem.json.InstanceSerialization$_Reference";
+        return JSON_CLASS_REFERENCE;
     }
 
     public Id getId() {
@@ -211,7 +235,7 @@ public class Referenceable extends Struct implements Serializable {
         super.toString(sb);
         sb.append(", id=");
         if (id != null) {
-            id.toString(sb);
+            id.asString(sb);
         }
         sb.append(", triats={");
         AtlasBaseTypeDef.dumpObjects(this.traits, sb);

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/v1/model/instance/Struct.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/v1/model/instance/Struct.java b/intg/src/main/java/org/apache/atlas/v1/model/instance/Struct.java
index 5fa4080..48d4389 100644
--- a/intg/src/main/java/org/apache/atlas/v1/model/instance/Struct.java
+++ b/intg/src/main/java/org/apache/atlas/v1/model/instance/Struct.java
@@ -20,6 +20,7 @@ package org.apache.atlas.v1.model.instance;
 
 
 import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
+import org.apache.commons.collections.MapUtils;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
@@ -29,9 +30,7 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
 import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONLY;
@@ -45,6 +44,8 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONL
 public class Struct implements Serializable {
     private static final long serialVersionUID = 1L;
 
+    public static final String JSON_CLASS_STRUCT = "org.apache.atlas.typesystem.json.InstanceSerialization$_Struct";
+
     private String              typeName;
     private Map<String, Object> values;
 
@@ -71,9 +72,20 @@ public class Struct implements Serializable {
         this.values   = values;
     }
 
+    public Struct(Map<String, Object> map) {
+        this();
+
+        if (map != null) {
+            this.typeName = Id.asString(map.get("typeName"));
+            this.values   = Id.asMap(map.get("values"));
+
+            this.normailze();
+        }
+    }
+
     // for serialization backward compatibility
     public String getJsonClass() {
-        return "org.apache.atlas.typesystem.json.InstanceSerialization$_Struct";
+        return JSON_CLASS_STRUCT;
     }
 
     public String getTypeName() {
@@ -118,6 +130,41 @@ public class Struct implements Serializable {
         }
     }
 
+    public void normailze() {
+        if (MapUtils.isEmpty(values)) {
+            return;
+        }
+
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            entry.setValue(normalizeAttributeValue(entry.getValue()));
+        }
+    }
+
+    private Object normalizeAttributeValue(Object value) {
+        if (value instanceof Map) {
+            Map    mapValue  = (Map) value;
+            String jsonClass = (String)mapValue.get("jsonClass");
+
+            if (jsonClass != null) {
+                if (Id.JSON_CLASS_ID.equals(jsonClass)) {
+                    value = new Id(mapValue);
+                } else if (Struct.JSON_CLASS_STRUCT.equals(jsonClass)) {
+                    value = new Struct(mapValue);
+                } else if (Referenceable.JSON_CLASS_REFERENCE.equals(jsonClass)) {
+                    value = new Referenceable(mapValue);
+                }
+            }
+        } else if (value instanceof List) {
+            List<Object> listValue       = (List) value;
+            List<Object> normalizedValue = new ArrayList<>(listValue.size());
+
+            for (Object val : listValue) {
+                normalizedValue.add(normalizeAttributeValue(val));
+            }
+        }
+
+        return value;
+    }
 
     @Override
     public boolean equals(Object o) {

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/v1/model/notification/EntityNotification.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/v1/model/notification/EntityNotification.java b/intg/src/main/java/org/apache/atlas/v1/model/notification/EntityNotification.java
index e4305dd..cb224af 100644
--- a/intg/src/main/java/org/apache/atlas/v1/model/notification/EntityNotification.java
+++ b/intg/src/main/java/org/apache/atlas/v1/model/notification/EntityNotification.java
@@ -129,6 +129,19 @@ public class EntityNotification implements Serializable {
         return traits;
     }
 
+    public void normalize() {
+        if (entity != null) {
+            entity.normailze();
+        }
+
+        if (traits != null) {
+            for (Struct trait : traits) {
+                if (trait != null) {
+                    trait.normailze();
+                }
+            }
+        }
+    }
 
     // ----- Object overrides --------------------------------------------------
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/v1/model/notification/HookNotification.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/v1/model/notification/HookNotification.java b/intg/src/main/java/org/apache/atlas/v1/model/notification/HookNotification.java
index 7be5e0b..ae0ec15 100644
--- a/intg/src/main/java/org/apache/atlas/v1/model/notification/HookNotification.java
+++ b/intg/src/main/java/org/apache/atlas/v1/model/notification/HookNotification.java
@@ -17,23 +17,18 @@
  */
 package org.apache.atlas.v1.model.notification;
 
-import com.google.gson.JsonParseException;
 import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
 import org.apache.atlas.v1.model.instance.Referenceable;
 import org.apache.atlas.v1.model.typedef.TypesDef;
-import org.apache.atlas.type.AtlasType;
 import org.apache.commons.lang.StringUtils;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
-import org.codehaus.jettison.json.JSONArray;
-import org.codehaus.jettison.json.JSONException;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -95,6 +90,8 @@ public class HookNotification {
             this.user = user;
         }
 
+        public void normalize() { }
+
         @Override
         public String toString() {
             return toString(new StringBuilder()).toString();
@@ -187,19 +184,8 @@ public class HookNotification {
 
         protected EntityCreateRequest(HookNotificationType type, List<Referenceable> entities, String user) {
             super(type, user);
-            this.entities = entities;
-        }
 
-        public EntityCreateRequest(String user, JSONArray jsonArray) {
-            super(HookNotificationType.ENTITY_CREATE, user);
-            entities = new ArrayList<>();
-            for (int index = 0; index < jsonArray.length(); index++) {
-                try {
-                    entities.add(AtlasType.fromV1Json(jsonArray.getString(index), Referenceable.class));
-                } catch (JSONException e) {
-                    throw new JsonParseException(e);
-                }
-            }
+            this.entities = entities;
         }
 
         public List<Referenceable> getEntities() {
@@ -211,6 +197,19 @@ public class HookNotification {
         }
 
         @Override
+        public void normalize() {
+            super.normalize();
+
+            if (entities != null) {
+                for (Referenceable entity : entities) {
+                    if (entity != null) {
+                        entity.normailze();
+                    }
+                }
+            }
+        }
+
+        @Override
         public StringBuilder toString(StringBuilder sb) {
             if (sb == null) {
                 sb = new StringBuilder();
@@ -322,6 +321,15 @@ public class HookNotification {
         }
 
         @Override
+        public void normalize() {
+            super.normalize();
+
+            if (entity != null) {
+                entity.normailze();
+            }
+        }
+
+        @Override
         public StringBuilder toString(StringBuilder sb) {
             if (sb == null) {
                 sb = new StringBuilder();

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/intg/src/main/java/org/apache/atlas/v1/model/typedef/Multiplicity.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/v1/model/typedef/Multiplicity.java b/intg/src/main/java/org/apache/atlas/v1/model/typedef/Multiplicity.java
index 8ac5732..653151b 100644
--- a/intg/src/main/java/org/apache/atlas/v1/model/typedef/Multiplicity.java
+++ b/intg/src/main/java/org/apache/atlas/v1/model/typedef/Multiplicity.java
@@ -18,14 +18,22 @@
 
 package org.apache.atlas.v1.model.typedef;
 
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonParser;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.Objects;
 
@@ -34,7 +42,8 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONL
 
 
 @JsonAutoDetect(getterVisibility=PUBLIC_ONLY, setterVisibility=PUBLIC_ONLY, fieldVisibility=NONE)
-@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonSerialize(using = Multiplicity.MultiplicitySerializer.class, include=JsonSerialize.Inclusion.NON_NULL)
+@JsonDeserialize(using = Multiplicity.MultiplicityDeserializer.class)
 @JsonIgnoreProperties(ignoreUnknown=true)
 @XmlRootElement
 @XmlAccessorType(XmlAccessType.PROPERTY)
@@ -110,4 +119,49 @@ public class Multiplicity implements Serializable {
     public int hashCode() {
         return Objects.hash(lower, upper, isUnique);
     }
+
+
+    static class MultiplicitySerializer extends JsonSerializer<Multiplicity> {
+        @Override
+        public void serialize(Multiplicity value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+            if (value != null) {
+                if (value.equals(Multiplicity.REQUIRED)) {
+                    jgen.writeString("required");
+                } else if (value.equals(Multiplicity.OPTIONAL)) {
+                    jgen.writeString("optional");
+                } else if (value.equals(Multiplicity.COLLECTION)) {
+                    jgen.writeString("collection");
+                } else if (value.equals(Multiplicity.SET)) {
+                    jgen.writeString("set");
+                }
+            }
+        }
+    }
+
+    static class MultiplicityDeserializer extends JsonDeserializer<Multiplicity> {
+        @Override
+        public Multiplicity deserialize(JsonParser parser, DeserializationContext context) throws IOException {
+            Multiplicity ret = null;
+
+            String value = parser.readValueAs(String.class);
+
+            if (value != null) {
+                if (value.equals("required")) {
+                    ret = new Multiplicity(Multiplicity.REQUIRED);
+                } else if (value.equals("optional")) {
+                    ret = new Multiplicity(Multiplicity.OPTIONAL);
+                } else if (value.equals("collection")) {
+                    ret = new Multiplicity(Multiplicity.COLLECTION);
+                } else if (value.equals("set")) {
+                    ret = new Multiplicity(Multiplicity.SET);
+                }
+            }
+
+            if (ret == null) {
+                ret = new Multiplicity();
+            }
+
+            return ret;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/notification/src/main/java/org/apache/atlas/hook/AtlasHook.java
----------------------------------------------------------------------
diff --git a/notification/src/main/java/org/apache/atlas/hook/AtlasHook.java b/notification/src/main/java/org/apache/atlas/hook/AtlasHook.java
index a225f3c..3bc4fba 100644
--- a/notification/src/main/java/org/apache/atlas/hook/AtlasHook.java
+++ b/notification/src/main/java/org/apache/atlas/hook/AtlasHook.java
@@ -101,17 +101,9 @@ public abstract class AtlasHook {
 
     protected abstract String getNumberOfRetriesPropertyKey();
 
-    protected void notifyEntities(String user, Collection<Referenceable> entities) {
-        JSONArray entitiesArray = new JSONArray();
-
-        for (Referenceable entity : entities) {
-            LOG.info("Adding entity for type: {}", entity.getTypeName());
-            final String entityJson = AtlasType.toV1Json(entity);
-            entitiesArray.put(entityJson);
-        }
-
+    protected void notifyEntities(String user, List<Referenceable> entities) {
         List<HookNotification.HookNotificationMessage> hookNotificationMessages = new ArrayList<>();
-        hookNotificationMessages.add(new HookNotification.EntityCreateRequest(user, entitiesArray));
+        hookNotificationMessages.add(new HookNotification.EntityCreateRequest(user, entities));
         notifyEntities(hookNotificationMessages);
     }
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/48daa7cb/notification/src/test/java/org/apache/atlas/notification/hook/HookNotificationTest.java
----------------------------------------------------------------------
diff --git a/notification/src/test/java/org/apache/atlas/notification/hook/HookNotificationTest.java b/notification/src/test/java/org/apache/atlas/notification/hook/HookNotificationTest.java
index ec2218c..3c87377 100644
--- a/notification/src/test/java/org/apache/atlas/notification/hook/HookNotificationTest.java
+++ b/notification/src/test/java/org/apache/atlas/notification/hook/HookNotificationTest.java
@@ -47,9 +47,7 @@ public class HookNotificationTest {
 
         Referenceable actualEntity1 = createRequest.getEntities().get(0);
         assertEquals(actualEntity1.getTypeName(), "sometype");
-        /* TODO:
         assertEquals(((Referenceable)actualEntity1.get("complex")).getTypeName(), "othertype");
-        */
         assertEquals(createRequest.getEntities().get(1).getTypeName(), "newtype");
     }