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 <[email protected]> Authored: Fri May 13 15:21:34 2016 +0530 Committer: Shwetha GS <[email protected]> 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"); + } + + } +}
