BUG-6757: revert fix for BUG-4456 91/47091/3
authorRobert Varga <rovarga@cisco.com>
Mon, 17 Oct 2016 20:19:54 +0000 (22:19 +0200)
committerRobert Varga <rovarga@cisco.com>
Tue, 18 Oct 2016 19:26:55 +0000 (21:26 +0200)
The fix has introduced a massive memory leak, which causes
all of temporary build objects to be retained in the final
SchemaContext.

Instead of the leak, add an explicit guard to detect
extensions (transitively) referencing themselves.

Change-Id: If90a4f9420866a6392ce97c71837915fa41ae0c8
Signed-off-by: Robert Varga <rovarga@cisco.com>
(cherry picked from commit e5226bf4ddd7a42aa13c392b2fc4d02d27ae2f74)
(cherry picked from commit 91f43b1ce6453fef9e04e6673e7637fb5dba8b38)

yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ExtensionStatementImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/AbstractEffectiveDocumentedNode.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/AbstractEffectiveModule.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/DeclaredEffectiveStatementBase.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/EffectiveStatementBase.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/ExtensionEffectiveStatementImpl.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug4456Test.java

index 0dd010deba6de5ad3b9cb564d007b5d973d3876a..74c6e67cb20c5783e61260090bbd37aa621f8fb5 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
 
+import java.util.HashSet;
+import java.util.Set;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
@@ -32,29 +34,48 @@ public class ExtensionStatementImpl extends AbstractDeclaredStatement<QName> imp
             .add(Rfc6020Mapping.STATUS, 0, 1)
             .build();
 
-    protected ExtensionStatementImpl(StmtContext<QName, ExtensionStatement,?> context) {
+    protected ExtensionStatementImpl(final StmtContext<QName, ExtensionStatement,?> context) {
         super(context);
     }
 
     public static class Definition extends AbstractStatementSupport<QName,ExtensionStatement,EffectiveStatement<QName,ExtensionStatement>> {
+        private static final ThreadLocal<Set<StmtContext<?, ?, ?>>> BUILDING = new ThreadLocal<>();
 
         public Definition() {
             super(Rfc6020Mapping.EXTENSION);
         }
 
         @Override
-        public QName parseArgumentValue(StmtContext<?,?,?> ctx, String value) {
+        public QName parseArgumentValue(final StmtContext<?,?,?> ctx, final String value) {
             return Utils.qNameFromArgument(ctx, value);
         }
 
         @Override
-        public ExtensionStatement createDeclared(StmtContext<QName, ExtensionStatement,?> ctx) {
+        public ExtensionStatement createDeclared(final StmtContext<QName, ExtensionStatement,?> ctx) {
             return new ExtensionStatementImpl(ctx);
         }
 
         @Override
-        public EffectiveStatement<QName,ExtensionStatement> createEffective(StmtContext<QName,ExtensionStatement,EffectiveStatement<QName,ExtensionStatement>> ctx) {
-           return new ExtensionEffectiveStatementImpl(ctx);
+        public EffectiveStatement<QName,ExtensionStatement> createEffective(
+                final StmtContext<QName,ExtensionStatement, EffectiveStatement<QName,ExtensionStatement>> ctx) {
+            Set<StmtContext<?, ?, ?>> building = BUILDING.get();
+            if (building == null) {
+                building = new HashSet<>();
+                BUILDING.set(building);
+            }
+
+            SourceException.throwIf(building.contains(ctx), ctx.getStatementSourceReference(),
+                "Extension %s references itself", ctx.getStatementArgument());
+
+            building.add(ctx);
+            try {
+                return new ExtensionEffectiveStatementImpl(ctx);
+            } finally {
+                building.remove(ctx);
+                if (building.isEmpty()) {
+                    BUILDING.remove();
+                }
+            }
         }
 
         @Override
@@ -63,8 +84,8 @@ public class ExtensionStatementImpl extends AbstractDeclaredStatement<QName> imp
         }
 
         @Override
-        public void onFullDefinitionDeclared(StmtContext.Mutable<QName, ExtensionStatement,
-                EffectiveStatement<QName, ExtensionStatement>> stmt) throws SourceException {
+        public void onFullDefinitionDeclared(final StmtContext.Mutable<QName, ExtensionStatement,
+                EffectiveStatement<QName, ExtensionStatement>> stmt) {
             super.onFullDefinitionDeclared(stmt);
             SUBSTATEMENT_VALIDATOR.validate(stmt);
         }
index 8c665d7a0a89d1b8688d8737f2e62bf389e01ab4..39a7adabc2c1f9aa74feb3ecd0065ad47105b466 100644 (file)
@@ -19,24 +19,14 @@ abstract class AbstractEffectiveDocumentedNode<A, D extends DeclaredStatement<A>
     private final String reference;
     private final Status status;
 
-    AbstractEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
-        this(ctx, true);
-    }
-
     /**
      * Constructor.
      *
      * @param ctx
      *            context of statement.
-     * @param buildUnknownSubstatements
-     *            if it is false, the unknown substatements are omitted from
-     *            build of effective substatements till the call of either
-     *            effectiveSubstatements or getOmittedUnknownSubstatements
-     *            method of EffectiveStatementBase class. The main purpose of
-     *            this is to allow the build of recursive extension definitions.
      */
-    AbstractEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx, boolean buildUnknownSubstatements) {
-        super(ctx, buildUnknownSubstatements);
+    protected AbstractEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
 
         DescriptionEffectiveStatementImpl descStmt = firstEffective(DescriptionEffectiveStatementImpl.class);
         if (descStmt != null) {
index 883b54cd5c5b9432dac4b11dcbc887919fc117c5..fd60af3a1bed7748fef68a56d01bc865e747e23d 100644 (file)
@@ -172,10 +172,7 @@ abstract class AbstractEffectiveModule<D extends DeclaredStatement<String>> exte
                 featuresInit.add((FeatureDefinition) effectiveStatement);
             }
             if (effectiveStatement instanceof ExtensionEffectiveStatementImpl) {
-                ExtensionEffectiveStatementImpl extensionDefinition = (ExtensionEffectiveStatementImpl) effectiveStatement;
-                extensionDefinition.initUnknownSchemaNodes();
-                extensionNodesInit
-                        .add(extensionDefinition);
+                extensionNodesInit.add((ExtensionEffectiveStatementImpl) effectiveStatement);
             }
             if (effectiveStatement instanceof DataSchemaNode) {
                 DataSchemaNode dataSchemaNode = (DataSchemaNode) effectiveStatement;
index 212a65e8137163f6383125cadf80cbb037e8375f..e414a46871f72e21d461d7039e04bd5be2c61846 100644 (file)
@@ -21,24 +21,14 @@ public abstract class DeclaredEffectiveStatementBase<A, D extends DeclaredStatem
     private final A argument;
     private final D declaredInstance;
 
-    public DeclaredEffectiveStatementBase(final StmtContext<A, D, ?> ctx) {
-        this(ctx, true);
-    }
-
     /**
      * Constructor.
      *
      * @param ctx
      *            context of statement.
-     * @param buildUnknownSubstatements
-     *            if it is false, the unknown substatements are omitted from
-     *            build of effective substatements till the call of either
-     *            effectiveSubstatements or getOmittedUnknownSubstatements
-     *            method of EffectiveStatementBase class. The main purpose of
-     *            this is to allow the build of recursive extension definitions.
      */
-    protected DeclaredEffectiveStatementBase(StmtContext<A, D, ?> ctx, final boolean buildUnknownSubstatements) {
-        super(ctx, buildUnknownSubstatements);
+    protected DeclaredEffectiveStatementBase(StmtContext<A, D, ?> ctx) {
+        super(ctx);
 
         this.argument = ctx.getStatementArgument();
         this.statementSource = ctx.getStatementSource();
@@ -75,4 +65,4 @@ public abstract class DeclaredEffectiveStatementBase<A, D extends DeclaredStatem
     public final D getDeclared() {
         return declaredInstance;
     }
-}
\ No newline at end of file
+}
index 7151db18f4c37124fc925074a37ddb331297b2cd..2af2b1fabe55fe1632f32670b07ee1d7104931aa 100644 (file)
@@ -45,26 +45,14 @@ public abstract class EffectiveStatementBase<A, D extends DeclaredStatement<A>>
     };
 
     private final List<? extends EffectiveStatement<?, ?>> substatements;
-    private final List<StatementContextBase<?, ?, ?>> unknownSubstatementsToBuild;
-
-    protected EffectiveStatementBase(final StmtContext<A, D, ?> ctx) {
-        this(ctx, true);
-    }
 
     /**
      * Constructor.
      *
      * @param ctx
      *            context of statement.
-     * @param buildUnknownSubstatements
-     *            if it is false, the unknown substatements are omitted from
-     *            build of effective substatements till the call of either
-     *            effectiveSubstatements or getOmittedUnknownSubstatements
-     *            method. The main purpose of this is to allow the build of
-     *            recursive extension definitions.
      */
-    protected EffectiveStatementBase(final StmtContext<A, D, ?> ctx, boolean buildUnknownSubstatements) {
-
+    protected EffectiveStatementBase(final StmtContext<A, D, ?> ctx) {
         final Collection<StatementContextBase<?, ?, ?>> effectiveSubstatements = ctx.effectiveSubstatements();
         final Collection<StatementContextBase<?, ?, ?>> substatementsInit = new ArrayList<>();
 
@@ -80,24 +68,8 @@ public abstract class EffectiveStatementBase<A, D extends DeclaredStatement<A>>
         }
         substatementsInit.addAll(effectiveSubstatements);
 
-        Collection<StatementContextBase<?, ?, ?>> substatementsToBuild = Collections2.filter(substatementsInit,
-                IS_SUPPORTED_TO_BUILD_EFFECTIVE);
-        if (!buildUnknownSubstatements) {
-            this.unknownSubstatementsToBuild = ImmutableList.copyOf(Collections2.filter(substatementsToBuild,
-                    IS_UNKNOWN_STATEMENT_CONTEXT));
-            substatementsToBuild = Collections2.filter(substatementsToBuild,
-                    Predicates.not(IS_UNKNOWN_STATEMENT_CONTEXT));
-        } else {
-            this.unknownSubstatementsToBuild = ImmutableList.of();
-        }
-
-        this.substatements = ImmutableList.copyOf(Collections2.transform(substatementsToBuild,
-                StmtContextUtils.buildEffective()));
-    }
-
-    Collection<EffectiveStatement<?, ?>> getOmittedUnknownSubstatements() {
-        return Collections2.transform(unknownSubstatementsToBuild,
-                StmtContextUtils.buildEffective());
+        this.substatements = ImmutableList.copyOf(Collections2.transform(Collections2.filter(substatementsInit,
+            IS_SUPPORTED_TO_BUILD_EFFECTIVE), StmtContextUtils.buildEffective()));
     }
 
     @Override
@@ -112,11 +84,7 @@ public abstract class EffectiveStatementBase<A, D extends DeclaredStatement<A>>
 
     @Override
     public final Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
-        if (unknownSubstatementsToBuild.isEmpty()) {
-            return substatements;
-        } else {
-            return ImmutableList.copyOf(Iterables.concat(substatements, getOmittedUnknownSubstatements()));
-        }
+        return substatements;
     }
 
     protected final <S extends EffectiveStatement<?, ?>> S firstEffective(final Class<S> type) {
index be762942462ba132a44667ef6a15061bc09ff238..4a13515b8d1f190bbbc23e2d06ebb1d675bf6dc5 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective;
 
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -26,15 +25,23 @@ public class ExtensionEffectiveStatementImpl extends AbstractEffectiveDocumented
     private final String argument;
     private final SchemaPath schemaPath;
 
-    private List<UnknownSchemaNode> unknownNodes;
+    private final List<UnknownSchemaNode> unknownNodes;
     private final boolean yin;
 
     public ExtensionEffectiveStatementImpl(
             final StmtContext<QName, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> ctx) {
-        super(ctx, false);
+        super(ctx);
         this.qname = ctx.getStatementArgument();
         this.schemaPath = ctx.getSchemaPath().get();
 
+        final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
+        for (EffectiveStatement<?, ?> unknownNode : effectiveSubstatements()) {
+            if (unknownNode instanceof UnknownSchemaNode) {
+                unknownNodesInit.add((UnknownSchemaNode) unknownNode);
+            }
+        }
+        this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
+
         // initFields
         ArgumentEffectiveStatementImpl argumentSubstatement = firstEffective(ArgumentEffectiveStatementImpl.class);
         if (argumentSubstatement != null) {
@@ -53,21 +60,6 @@ public class ExtensionEffectiveStatementImpl extends AbstractEffectiveDocumented
         }
     }
 
-    void initUnknownSchemaNodes() {
-        if (unknownNodes != null) {
-            return;
-        }
-
-        Collection<EffectiveStatement<?, ?>> buildedUnknownNodes = getOmittedUnknownSubstatements();
-        List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
-        for (EffectiveStatement<?, ?> unknownNode : buildedUnknownNodes) {
-            if (unknownNode instanceof UnknownSchemaNode) {
-                unknownNodesInit.add((UnknownSchemaNode) unknownNode);
-            }
-        }
-        this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
-    }
-
     @Override
     public QName getQName() {
         return qname;
@@ -80,9 +72,6 @@ public class ExtensionEffectiveStatementImpl extends AbstractEffectiveDocumented
 
     @Override
     public List<UnknownSchemaNode> getUnknownSchemaNodes() {
-        if(unknownNodes == null) {
-            initUnknownSchemaNodes();
-        }
         return unknownNodes;
     }
 
index 88eb8cdba4adfa440839487a00e803ad6251094e..5af276c546c28cb055a4db57710d45a0259dc089 100644 (file)
@@ -25,7 +25,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 
 public class Bug4456Test {
-    @Test
+    @Test(expected=SourceException.class)
     public void test() throws IOException, URISyntaxException, SourceException, ReactorException {
         SchemaContext schema = StmtTestUtils.parseYangSources("/bugs/bug4456");
         assertNotNull(schema);