You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by sh...@apache.org on 2016/05/13 09:51:40 UTC
incubator-atlas git commit: ATLAS-645 FieldMapping.output() results
in stack overflow when instances reference each other (dkantor via shwethags)
Repository: incubator-atlas
Updated Branches:
refs/heads/master 454feb47a -> 28991c52b
ATLAS-645 FieldMapping.output() results in stack overflow when instances reference each other (dkantor via shwethags)
Project: http://git-wip-us.apache.org/repos/asf/incubator-atlas/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-atlas/commit/28991c52
Tree: http://git-wip-us.apache.org/repos/asf/incubator-atlas/tree/28991c52
Diff: http://git-wip-us.apache.org/repos/asf/incubator-atlas/diff/28991c52
Branch: refs/heads/master
Commit: 28991c52b3e74278bd49c1d79674ff3f9b49a4f7
Parents: 454feb4
Author: Shwetha GS <ss...@hortonworks.com>
Authored: Fri May 13 15:21:34 2016 +0530
Committer: Shwetha GS <ss...@hortonworks.com>
Committed: Fri May 13 15:21:34 2016 +0530
----------------------------------------------------------------------
release-log.txt | 1 +
.../persistence/ReferenceableInstance.java | 5 +-
.../typesystem/persistence/StructInstance.java | 24 +--
.../typesystem/types/AbstractDataType.java | 22 ++-
.../atlas/typesystem/types/AttributeInfo.java | 36 +++--
.../atlas/typesystem/types/ClassType.java | 5 +-
.../atlas/typesystem/types/DataTypes.java | 3 +-
.../atlas/typesystem/types/FieldMapping.java | 78 +++++++---
.../typesystem/types/HierarchicalType.java | 55 ++++++-
.../atlas/typesystem/types/IDataType.java | 21 ++-
.../atlas/typesystem/types/Multiplicity.java | 3 +-
.../atlas/typesystem/types/StructType.java | 63 +++++++-
.../atlas/typesystem/types/TraitType.java | 5 +-
.../typesystem/types/TypedStructHandler.java | 6 +-
.../typesystem/types/FieldMappingTest.java | 151 +++++++++++++++++++
15 files changed, 408 insertions(+), 70 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index baa4c67..9892b0c 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -20,6 +20,7 @@ ATLAS-409 Atlas will not import avro tables with schema read from a file (dosset
ATLAS-379 Create sqoop and falcon metadata addons (venkatnrangan,bvellanki,sowmyaramesh via shwethags)
ALL CHANGES:
+ATLAS-645 FieldMapping.output() results in stack overflow when instances reference each other (dkantor via shwethags)
ATLAS-733 UI: "undefined" XHR request is made for every entity GET page request. (kevalbhatt18 via yhemanth)
ATLAS-663,ATLAS-673 Install Setup: SOLR (tbeerbower via sumasai)
ATLAS-629 Kafka messages in ATLAS_HOOK might be lost in HA mode at the instant of failover. (yhemanth)
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/ReferenceableInstance.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/ReferenceableInstance.java b/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/ReferenceableInstance.java
index 31ef49d..561cb62 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/ReferenceableInstance.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/ReferenceableInstance.java
@@ -20,7 +20,9 @@ package org.apache.atlas.typesystem.persistence;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+
import org.apache.atlas.AtlasException;
+import org.apache.atlas.typesystem.IReferenceableInstance;
import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.ITypedReferenceableInstance;
import org.apache.atlas.typesystem.ITypedStruct;
@@ -33,6 +35,7 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Date;
+import java.util.HashSet;
/*
* @todo handle names prefixed by traitName.
@@ -89,7 +92,7 @@ public class ReferenceableInstance extends StructInstance implements ITypedRefer
StringBuilder buf = new StringBuilder();
String prefix = "";
- fieldMapping.output(this, buf, prefix);
+ fieldMapping.output(this, buf, prefix, new HashSet<IReferenceableInstance>());
return buf.toString();
} catch (AtlasException me) {
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/StructInstance.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/StructInstance.java b/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/StructInstance.java
index af62442..6fb2087 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/StructInstance.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/persistence/StructInstance.java
@@ -20,8 +20,8 @@ package org.apache.atlas.typesystem.persistence;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+
import org.apache.atlas.AtlasException;
-import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.ITypedStruct;
import org.apache.atlas.typesystem.types.AttributeInfo;
import org.apache.atlas.typesystem.types.ClassType;
@@ -31,7 +31,6 @@ import org.apache.atlas.typesystem.types.EnumValue;
import org.apache.atlas.typesystem.types.FieldMapping;
import org.apache.atlas.typesystem.types.StructType;
import org.apache.atlas.typesystem.types.TypeSystem;
-import org.apache.atlas.typesystem.types.TypeUtils;
import org.apache.atlas.typesystem.types.ValueConversionException;
import org.apache.atlas.utils.MD5Utils;
@@ -724,32 +723,13 @@ public class StructInstance implements ITypedStruct {
strings[pos] = val;
}
- public void output(IStruct s, Appendable buf, String prefix) throws AtlasException {
- TypeUtils.outputVal("{", buf, prefix);
- if (s == null) {
- TypeUtils.outputVal("<null>\n", buf, "");
- return;
- }
- TypeUtils.outputVal("\n", buf, "");
- String fieldPrefix = prefix + "\t";
- for (Map.Entry<String, AttributeInfo> e : fieldMapping.fields.entrySet()) {
- String attrName = e.getKey();
- AttributeInfo i = e.getValue();
- Object aVal = s.get(attrName);
- TypeUtils.outputVal(attrName + " : ", buf, fieldPrefix);
- i.dataType().output(aVal, buf, "");
- TypeUtils.outputVal("\n", buf, "");
- }
- TypeUtils.outputVal("\n}\n", buf, "");
- }
-
@Override
public String toString() {
try {
StringBuilder buf = new StringBuilder();
String prefix = "";
- fieldMapping.output(this, buf, prefix);
+ fieldMapping.output(this, buf, prefix, null);
return buf.toString();
} catch (AtlasException me) {
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/AbstractDataType.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/AbstractDataType.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/AbstractDataType.java
index 92be3c7..dc9cdf2 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/AbstractDataType.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/AbstractDataType.java
@@ -22,7 +22,9 @@ import com.google.common.collect.ImmutableSortedMap;
import org.apache.atlas.AtlasException;
+import java.io.IOException;
import java.util.Map;
+import java.util.Set;
abstract class AbstractDataType<T> implements IDataType<T> {
@@ -44,7 +46,7 @@ abstract class AbstractDataType<T> implements IDataType<T> {
}
@Override
- public void output(T val, Appendable buf, String prefix) throws AtlasException {
+ public void output(T val, Appendable buf, String prefix, Set<T> inProcess) throws AtlasException {
if (val instanceof Map) {
ImmutableSortedMap immutableSortedMap = ImmutableSortedMap.copyOf((Map) val);
TypeUtils.outputVal(val == null ? "<null>" : immutableSortedMap.toString(), buf, prefix);
@@ -53,6 +55,24 @@ abstract class AbstractDataType<T> implements IDataType<T> {
}
}
+ @Override
+ public void output(Appendable buf, Set<String> typesInProcess) throws AtlasException {
+
+ try {
+ buf.append(toString());
+ } catch (IOException e) {
+ throw new AtlasException(e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "{name=" + name + ", description=" + description + "}";
+ }
+
/**
* Validate that current definition can be updated with the new definition
* @param newType
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/AttributeInfo.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/AttributeInfo.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/AttributeInfo.java
index e748579..9cb0d0d 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/AttributeInfo.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/AttributeInfo.java
@@ -22,7 +22,10 @@ import org.apache.atlas.AtlasException;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
+import java.io.IOException;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
public class AttributeInfo {
public final String name;
@@ -60,15 +63,30 @@ public class AttributeInfo {
@Override
public String toString() {
- return "AttributeInfo{" +
- "name='" + name + '\'' +
- ", dataType=" + dataType +
- ", multiplicity=" + multiplicity +
- ", isComposite=" + isComposite +
- ", isUnique=" + isUnique +
- ", isIndexable=" + isIndexable +
- ", reverseAttributeName='" + reverseAttributeName + '\'' +
- '}';
+ StringBuilder buf = new StringBuilder();
+ try {
+ output(buf, new HashSet<String>());
+ } catch (AtlasException e) {
+ throw new RuntimeException(e);
+ }
+ return buf.toString();
+ }
+
+ public void output(Appendable buf, Set<String> typesInProcess) throws AtlasException {
+ try {
+ buf.append("{name=").append(name);
+ buf.append(", dataType=");
+ dataType.output(buf, typesInProcess);
+ buf.append(", multiplicity=").append(multiplicity.toString());
+ buf.append(", isComposite=").append(Boolean.toString(isComposite));
+ buf.append(", isUnique=").append(Boolean.toString(isUnique));
+ buf.append(", isIndexable=").append(Boolean.toString(isIndexable));
+ buf.append(", reverseAttributeName=").append(reverseAttributeName);
+ buf.append('}');
+ }
+ catch(IOException e) {
+ throw new AtlasException(e);
+ }
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/ClassType.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/ClassType.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/ClassType.java
index 90cf3cc..c56987a 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/ClassType.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/ClassType.java
@@ -41,6 +41,7 @@ import java.security.MessageDigest;
import java.util.Date;
import java.util.List;
import java.util.Map;
+import java.util.Set;
public class ClassType extends HierarchicalType<ClassType, IReferenceableInstance>
implements IConstructableType<IReferenceableInstance, ITypedReferenceableInstance> {
@@ -207,8 +208,8 @@ public class ClassType extends HierarchicalType<ClassType, IReferenceableInstanc
}
@Override
- public void output(IReferenceableInstance s, Appendable buf, String prefix) throws AtlasException {
- fieldMapping.output(s, buf, prefix);
+ public void output(IReferenceableInstance s, Appendable buf, String prefix, Set<IReferenceableInstance> inProcess) throws AtlasException {
+ fieldMapping.output(s, buf, prefix, inProcess);
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/DataTypes.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/DataTypes.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/DataTypes.java
index 55ec91f..41b3427 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/DataTypes.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/DataTypes.java
@@ -38,6 +38,7 @@ import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
public class DataTypes {
@@ -425,7 +426,7 @@ public class DataTypes {
}
@Override
- public void output(Date val, Appendable buf, String prefix) throws AtlasException {
+ public void output(Date val, Appendable buf, String prefix, Set<Date> inProcess) throws AtlasException {
TypeUtils.outputVal(val == null ? "<null>" : TypeSystem.getInstance().getDateFormat().format(val), buf,
prefix);
}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/FieldMapping.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/FieldMapping.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/FieldMapping.java
index 36149ba..a2b3db2 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/FieldMapping.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/FieldMapping.java
@@ -23,7 +23,9 @@ import org.apache.atlas.typesystem.IReferenceableInstance;
import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.persistence.Id;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
public class FieldMapping {
@@ -70,7 +72,7 @@ public class FieldMapping {
this.numReferenceables = numReferenceables;
}
- protected void outputFields(IStruct s, Appendable buf, String fieldPrefix) throws AtlasException {
+ protected void outputFields(IStruct s, Appendable buf, String fieldPrefix, Set<? extends IStruct> inProcess) throws AtlasException {
for (Map.Entry<String, AttributeInfo> e : fields.entrySet()) {
String attrName = e.getKey();
AttributeInfo i = e.getValue();
@@ -79,52 +81,82 @@ public class FieldMapping {
if (aVal != null && aVal instanceof Id) {
TypeUtils.outputVal(aVal.toString(), buf, "");
} else {
- i.dataType().output(aVal, buf, fieldPrefix);
+ i.dataType().output(aVal, buf, fieldPrefix, inProcess);
}
TypeUtils.outputVal("\n", buf, "");
}
}
- public void output(IStruct s, Appendable buf, String prefix) throws AtlasException {
+ public void output(IStruct s, Appendable buf, String prefix, Set<IStruct> inProcess) throws AtlasException {
if (s == null) {
TypeUtils.outputVal("<null>\n", buf, "");
return;
}
- TypeUtils.outputVal("{", buf, prefix);
- TypeUtils.outputVal("\n", buf, "");
- String fieldPrefix = prefix + "\t";
+ if (inProcess == null) {
+ inProcess = new HashSet<>();
+ }
+ else if (inProcess.contains(s)) {
+ // Avoid infinite recursion when structs reference each other.
+ return;
+ }
+ inProcess.add(s);
+
+ try {
+ TypeUtils.outputVal("{", buf, prefix);
+
+ TypeUtils.outputVal("\n", buf, "");
+ String fieldPrefix = prefix + "\t";
- outputFields(s, buf, fieldPrefix);
+ outputFields(s, buf, fieldPrefix, inProcess);
- TypeUtils.outputVal("}", buf, prefix);
+ TypeUtils.outputVal("}", buf, prefix);
+ }
+ finally {
+ inProcess.remove(s);
+ }
}
- public void output(IReferenceableInstance s, Appendable buf, String prefix) throws AtlasException {
+ public void output(IReferenceableInstance s, Appendable buf, String prefix, Set<IReferenceableInstance> inProcess) throws AtlasException {
if (s == null) {
TypeUtils.outputVal("<null>\n", buf, "");
return;
}
- TypeUtils.outputVal("{", buf, prefix);
- TypeUtils.outputVal("\n", buf, "");
- String fieldPrefix = prefix + "\t";
+ if (inProcess == null) {
+ inProcess = new HashSet<>();
+ }
+ else if (inProcess.contains(s)) {
+ // Avoid infinite recursion when structs reference each other.
+ return;
+ }
+ inProcess.add(s);
- TypeUtils.outputVal("id : ", buf, fieldPrefix);
- TypeUtils.outputVal(s.getId().toString(), buf, "");
- TypeUtils.outputVal("\n", buf, "");
+ try {
+ TypeUtils.outputVal("{", buf, prefix);
- outputFields(s, buf, fieldPrefix);
+ TypeUtils.outputVal("\n", buf, "");
+ String fieldPrefix = prefix + "\t";
- TypeSystem ts = TypeSystem.getInstance();
+ TypeUtils.outputVal("id : ", buf, fieldPrefix);
+ TypeUtils.outputVal(s.getId().toString(), buf, "");
+ TypeUtils.outputVal("\n", buf, "");
- for (String sT : s.getTraits()) {
- TraitType tt = ts.getDataType(TraitType.class, sT);
- TypeUtils.outputVal(sT + " : ", buf, fieldPrefix);
- tt.output(s.getTrait(sT), buf, fieldPrefix);
- }
+ outputFields(s, buf, fieldPrefix, inProcess);
+
+ TypeSystem ts = TypeSystem.getInstance();
- TypeUtils.outputVal("}", buf, prefix);
+ for (String sT : s.getTraits()) {
+ TraitType tt = ts.getDataType(TraitType.class, sT);
+ TypeUtils.outputVal(sT + " : ", buf, fieldPrefix);
+ tt.output(s.getTrait(sT), buf, fieldPrefix, null);
+ }
+
+ TypeUtils.outputVal("}", buf, prefix);
+ }
+ finally {
+ inProcess.remove(s);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/HierarchicalType.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/HierarchicalType.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/HierarchicalType.java
index 89fcea6..859ec72 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/HierarchicalType.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/HierarchicalType.java
@@ -21,12 +21,14 @@ package org.apache.atlas.typesystem.types;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.UnmodifiableIterator;
import org.apache.atlas.AtlasException;
import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.persistence.DownCastStructInstance;
import org.apache.atlas.typesystem.types.TypeUtils.Pair;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -367,9 +369,58 @@ public abstract class HierarchicalType<ST extends HierarchicalType, T> extends A
@Override
public String toString() {
+ StringBuilder buf = new StringBuilder();
+ try {
+ output(buf, new HashSet<String>());
+ }
+ catch (AtlasException e) {
+ throw new RuntimeException(e);
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public void output(Appendable buf, Set<String> typesInProcess) throws AtlasException {
+
+ if (typesInProcess == null) {
+ typesInProcess = new HashSet<>();
+ }
+ else if (typesInProcess.contains(name)) {
+ // Avoid infinite recursion on bi-directional reference attributes.
+ try {
+ buf.append(name);
+ } catch (IOException e) {
+ throw new AtlasException(e);
+ }
+ return;
+ }
- return "[name=" + name + ", description=" + description +
- ", superTypes=" + superTypes + ", immediateAttrs=" + immediateAttrs + "]";
+ typesInProcess.add(name);
+ try {
+ buf.append(getClass().getSimpleName()).append('{');
+ buf.append("name=").append(name);
+ buf.append(", description=").append(description);
+ buf.append(", superTypes=").append(superTypes.toString());
+ buf.append(", immediateAttrs=[");
+ UnmodifiableIterator<AttributeInfo> it = immediateAttrs.iterator();
+ while (it.hasNext()) {
+ AttributeInfo attrInfo = it.next();
+ attrInfo.output(buf, typesInProcess);
+ if (it.hasNext()) {
+ buf.append(", ");
+ }
+ else {
+ buf.append(']');
+ }
+ }
+ buf.append("}");
+ }
+ catch(IOException e) {
+ throw new AtlasException(e);
+ }
+ finally {
+ typesInProcess.remove(name);
+ }
}
public Set<String> getAllSuperTypeNames() {
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/IDataType.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/IDataType.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/IDataType.java
index 373ad2c..85ddee7 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/IDataType.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/IDataType.java
@@ -21,6 +21,7 @@ package org.apache.atlas.typesystem.types;
import org.apache.atlas.AtlasException;
import java.security.MessageDigest;
+import java.util.Set;
public interface IDataType<T> {
String getName();
@@ -29,7 +30,25 @@ public interface IDataType<T> {
DataTypes.TypeCategory getTypeCategory();
- void output(T val, Appendable buf, String prefix) throws AtlasException;
+ /**
+ * Output a string representation of a value instance of this type.
+ *
+ * @param val
+ * @param buf
+ * @param prefix
+ * @param inProcess
+ * @throws AtlasException
+ */
+ void output(T val, Appendable buf, String prefix, Set<T> inProcess) throws AtlasException;
+
+ /**
+ * Output a string representation of this type.
+ *
+ * @param buf
+ * @param typesInProcess
+ * @throws AtlasException
+ */
+ void output(Appendable buf, Set<String> typesInProcess) throws AtlasException;
void validateUpdate(IDataType newType) throws TypeUpdateException;
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/Multiplicity.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/Multiplicity.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/Multiplicity.java
index a54dabc..06da32e 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/Multiplicity.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/Multiplicity.java
@@ -79,8 +79,7 @@ public final class Multiplicity {
@Override
public String toString() {
- return "Multiplicity{" +
- "lower=" + lower +
+ return "{lower=" + lower +
", upper=" + upper +
", isUnique=" + isUnique +
'}';
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/StructType.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/StructType.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/StructType.java
index 54e344f..6f40c1d 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/StructType.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/StructType.java
@@ -18,12 +18,16 @@
package org.apache.atlas.typesystem.types;
+import java.io.IOException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.atlas.AtlasException;
import org.apache.atlas.typesystem.IStruct;
@@ -182,8 +186,63 @@ public class StructType extends AbstractDataType<IStruct> implements IConstructa
}
@Override
- public void output(IStruct s, Appendable buf, String prefix) throws AtlasException {
- handler.output(s, buf, prefix);
+ public void output(IStruct s, Appendable buf, String prefix, Set<IStruct> inProcess) throws AtlasException {
+ handler.output(s, buf, prefix, inProcess);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ try {
+ output(buf, new HashSet<String>());
+ }
+ catch (AtlasException e) {
+ throw new RuntimeException(e);
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public void output(Appendable buf, Set<String> typesInProcess) throws AtlasException {
+
+ if (typesInProcess == null) {
+ typesInProcess = new HashSet<>();
+ }
+ else if (typesInProcess.contains(name)) {
+ // Avoid infinite recursion on bi-directional reference attributes.
+ try {
+ buf.append(name);
+ } catch (IOException e) {
+ throw new AtlasException(e);
+ }
+ return;
+ }
+
+ typesInProcess.add(name);
+ try {
+ buf.append(getClass().getSimpleName());
+ buf.append("{name=").append(name);
+ buf.append(", description=").append(description);
+ buf.append(", fieldMapping.fields=[");
+ Iterator<AttributeInfo> it = fieldMapping.fields.values().iterator();
+ while (it.hasNext()) {
+ AttributeInfo attrInfo = it.next();
+ attrInfo.output(buf, typesInProcess);
+ if (it.hasNext()) {
+ buf.append(", ");
+ }
+ else {
+ buf.append(']');
+ }
+ }
+ buf.append("}");
+ }
+ catch(IOException e) {
+ throw new AtlasException(e);
+ }
+ finally {
+ typesInProcess.remove(name);
+ }
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/TraitType.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/TraitType.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/TraitType.java
index 84c22bf..f23bf5b 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/TraitType.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/TraitType.java
@@ -28,6 +28,7 @@ import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.List;
import java.util.Map;
+import java.util.Set;
public class TraitType extends HierarchicalType<TraitType, IStruct>
implements IConstructableType<IStruct, ITypedStruct> {
@@ -63,8 +64,8 @@ public class TraitType extends HierarchicalType<TraitType, IStruct>
}
@Override
- public void output(IStruct s, Appendable buf, String prefix) throws AtlasException {
- handler.output(s, buf, prefix);
+ public void output(IStruct s, Appendable buf, String prefix, Set<IStruct> inProcess) throws AtlasException {
+ handler.output(s, buf, prefix, inProcess);
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypedStructHandler.java
----------------------------------------------------------------------
diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypedStructHandler.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypedStructHandler.java
index da246d6..b97669a 100755
--- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypedStructHandler.java
+++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypedStructHandler.java
@@ -20,6 +20,7 @@ package org.apache.atlas.typesystem.types;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+
import org.apache.atlas.AtlasException;
import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.ITypedStruct;
@@ -32,6 +33,7 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.Map;
+import java.util.Set;
public class TypedStructHandler {
@@ -104,8 +106,8 @@ public class TypedStructHandler {
fieldMapping.numReferenceables == 0 ? null : new Id[fieldMapping.numReferenceables]);
}
- public void output(IStruct s, Appendable buf, String prefix) throws AtlasException {
- fieldMapping.output(s, buf, prefix);
+ public void output(IStruct s, Appendable buf, String prefix, Set<IStruct> inProcess) throws AtlasException {
+ fieldMapping.output(s, buf, prefix, inProcess);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/28991c52/typesystem/src/test/java/org/apache/atlas/typesystem/types/FieldMappingTest.java
----------------------------------------------------------------------
diff --git a/typesystem/src/test/java/org/apache/atlas/typesystem/types/FieldMappingTest.java b/typesystem/src/test/java/org/apache/atlas/typesystem/types/FieldMappingTest.java
new file mode 100644
index 0000000..0259ade
--- /dev/null
+++ b/typesystem/src/test/java/org/apache/atlas/typesystem/types/FieldMappingTest.java
@@ -0,0 +1,151 @@
+/**
+ * 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.atlas.typesystem.types;
+
+import java.util.HashSet;
+
+import org.apache.atlas.typesystem.IReferenceableInstance;
+import org.apache.atlas.typesystem.ITypedReferenceableInstance;
+import org.apache.atlas.typesystem.ITypedStruct;
+import org.apache.atlas.typesystem.TypesDef;
+import org.apache.atlas.typesystem.types.AttributeDefinition;
+import org.apache.atlas.typesystem.types.ClassType;
+import org.apache.atlas.typesystem.types.EnumTypeDefinition;
+import org.apache.atlas.typesystem.types.HierarchicalTypeDefinition;
+import org.apache.atlas.typesystem.types.Multiplicity;
+import org.apache.atlas.typesystem.types.StructTypeDefinition;
+import org.apache.atlas.typesystem.types.TraitType;
+import org.apache.atlas.typesystem.types.TypeSystem;
+import org.apache.atlas.typesystem.types.utils.TypesUtil;
+import org.testng.Assert;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+
+/**
+ * Unit test for {@link FieldMapping}
+ *
+ */
+public class FieldMappingTest {
+
+ @BeforeTest
+ public void beforeTest() throws Exception {
+ TypeSystem typeSystem = TypeSystem.getInstance();
+ typeSystem.reset();
+ }
+
+ @Test
+ public void testOutputReferenceableInstance() throws Exception {
+ // ATLAS-645: verify that FieldMapping.output(IReferenceableInstance)
+ // does not infinitely recurse when ITypedReferenceableInstance's reference each other.
+ HierarchicalTypeDefinition<ClassType> valueDef = TypesUtil.createClassTypeDef("Value",
+ ImmutableSet.<String>of(),
+ new AttributeDefinition("owner", "Owner", Multiplicity.OPTIONAL, false, null));
+
+ // Define class type with reference, where the value is a class reference to Value.
+ HierarchicalTypeDefinition<ClassType> ownerDef = TypesUtil.createClassTypeDef("Owner",
+ ImmutableSet.<String>of(),
+ new AttributeDefinition("value", "Value", Multiplicity.OPTIONAL, false, null));
+ TypesDef typesDef = TypesUtil.getTypesDef(ImmutableList.<EnumTypeDefinition>of(),
+ ImmutableList.<StructTypeDefinition>of(), ImmutableList.<HierarchicalTypeDefinition<TraitType>>of(),
+ ImmutableList.of(ownerDef, valueDef));
+
+ TypeSystem typeSystem = TypeSystem.getInstance();
+ typeSystem.defineTypes(typesDef);
+ ClassType ownerType = typeSystem.getDataType(ClassType.class, "Owner");
+
+ // Prior to fix for ATLAS-645, this call would throw a StackOverflowError
+ try {
+ ownerType.toString();
+ }
+ catch (StackOverflowError e) {
+ Assert.fail("Infinite recursion in ClassType.toString() caused StackOverflowError");
+ }
+
+ ClassType valueType = typeSystem.getDataType(ClassType.class, "Value");
+
+ // Create instances of Owner and Value that reference each other.
+ ITypedReferenceableInstance ownerInstance = ownerType.createInstance();
+ ITypedReferenceableInstance valueInstance = valueType.createInstance();
+ // Set Owner.value reference to Value instance.
+ ownerInstance.set("value", valueInstance);
+ // Set Value.owner reference on Owner instance.
+ valueInstance.set("owner", ownerInstance);
+
+ // Prior to fix for ATLAS-645, this call would throw a StackOverflowError
+ try {
+ ownerInstance.fieldMapping().output(ownerInstance, new StringBuilder(), "", new HashSet<IReferenceableInstance>());
+ }
+ catch (StackOverflowError e) {
+ Assert.fail("Infinite recursion in FieldMapping.output() caused StackOverflowError");
+ }
+ }
+
+ @Test
+ public void testOutputStruct() throws Exception {
+ // ATLAS-645: verify that FieldMapping.output(IStruct) does not infinitely recurse
+ // when an IStruct and ITypedReferenceableInstance reference each other.
+ HierarchicalTypeDefinition<ClassType> valueDef = TypesUtil.createClassTypeDef("Value",
+ ImmutableSet.<String>of(),
+ new AttributeDefinition("owner", "Owner", Multiplicity.OPTIONAL, false, null));
+
+
+ // Define struct type with reference, where the value is a class reference to Value.
+ StructTypeDefinition ownerDef = TypesUtil.createStructTypeDef("Owner",
+ new AttributeDefinition("value", "Value", Multiplicity.OPTIONAL, false, null));
+
+ TypesDef typesDef = TypesUtil.getTypesDef(ImmutableList.<EnumTypeDefinition>of(),
+ ImmutableList.of(ownerDef), ImmutableList.<HierarchicalTypeDefinition<TraitType>>of(),
+ ImmutableList.of(valueDef));
+
+ TypeSystem typeSystem = TypeSystem.getInstance();
+ typeSystem.reset();
+ typeSystem.defineTypes(typesDef);
+ StructType ownerType = typeSystem.getDataType(StructType.class, "Owner");
+ ClassType valueType = typeSystem.getDataType(ClassType.class, "Value");
+
+ // Prior to fix for ATLAS-645, this call would throw a StackOverflowError
+ try {
+ ownerType.toString();
+ }
+ catch (StackOverflowError e) {
+ Assert.fail("Infinite recursion in StructType.toString() caused StackOverflowError");
+ }
+
+
+ // Create instances of Owner and Value that reference each other.
+ ITypedStruct ownerInstance = ownerType.createInstance();
+ ITypedReferenceableInstance valueInstance = valueType.createInstance();
+ // Set Owner.value reference to Value instance.
+ ownerInstance.set("value", valueInstance);
+ // Set Value.owner reference on Owner instance.
+ valueInstance.set("owner", ownerInstance);
+
+ // Prior to fix for ATLAS-645, this call would throw a StackOverflowError
+ try {
+ ownerInstance.fieldMapping().output(ownerInstance, new StringBuilder(), "", null);
+ }
+ catch (StackOverflowError e) {
+ Assert.fail("Infinite recursion in FieldMapping.output() caused StackOverflowError");
+ }
+
+ }
+}