Author: rbaxter85
Date: Thu Mar 7 14:26:01 2013
New Revision: 1453895
URL: http://svn.apache.org/r1453895
Log:
Do enhancement for FeatureRegistry to detect feature dependency loop problem
SHINDIG-1877
Comitted For Jiaging Guo
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureRegistryTest.java
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java?rev=1453895&r1=1453894&r2=1453895&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java
Thu Mar 7 14:26:01 2013
@@ -46,7 +46,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
@@ -693,6 +695,10 @@ public class FeatureRegistry {
return;
}
+ // Detect feature dependency loop
+ Set<String> tempList = new HashSet<String>();
+ this.checkDependencyLoop(this, tempList);
+
this.nodeDepth = 0;
this.transitiveDeps = Lists.newLinkedList();
this.transitiveDeps.add(this);
@@ -703,10 +709,6 @@ public class FeatureRegistry {
while (!toTraverse.isEmpty()) {
Pair<FeatureNode, Pair<Integer, String>> next = toTraverse.poll();
String debug = next.two.two + (next.two.one > 0 ? " -> " : "") +
next.one.name;
- if (next.one == this && next.two.one != 0) {
- throw new GadgetException(GadgetException.Code.INVALID_CONFIG,
- "Feature dep loop detected: " + debug);
- }
// Breadth-first list of dependencies.
this.transitiveDeps.add(next.one);
this.nodeDepth = Math.max(this.nodeDepth, next.two.one);
@@ -722,5 +724,30 @@ public class FeatureRegistry {
public List<FeatureNode> getTransitiveDeps() {
return this.transitiveDeps;
}
+
+ /**
+ * Check whether current feature node has dependency loop
+ * @param featureNode
+ * feature node which needs to check whether it has dependency loop.
+ * @param dependencyList
+ * used to check whether current branch of feature dependency tree has
dependency loop or not.
+ * @throws GadgetException
+ * a new GadgetException will be thrown if a loop is detected.
+ */
+ private void checkDependencyLoop(FeatureNode featureNode, Set<String>
dependencyList) throws GadgetException {
+ String featureNodeName = featureNode.name;
+ if (dependencyList.contains(featureNodeName)) {
+ // If dependencyList already contains this feature node, then
current branch has dependency loop.
+ throw new GadgetException(GadgetException.Code.INVALID_CONFIG,
"Feature " + featureNodeName + " has dependency loop problem.");
+ }
+ // Add the current dependency into branch.
+ dependencyList.add(featureNodeName);
+ List<FeatureNode> deps = featureNode.getDepList();
+ for (FeatureNode f : deps) {
+ checkDependencyLoop(f, dependencyList);
+ // Remove the part which already has been verified.
+ dependencyList.remove(f.name);
+ }
+ }
}
}
Modified:
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureRegistryTest.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureRegistryTest.java?rev=1453895&r1=1453894&r2=1453895&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureRegistryTest.java
(original)
+++
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureRegistryTest.java
Thu Mar 7 14:26:01 2013
@@ -663,6 +663,106 @@ public class FeatureRegistryTest {
assertEquals("top();gadget();", resources.get(3).getContent());
}
+ @Test(expected = GadgetException.class)
+ public void testCheckDependencyLoopWithLoopDependencyError() throws
Exception {
+ TestFeatureRegistry.Builder builder = TestFeatureRegistry.newBuilder();
+ Uri featureA, featureB, featureC, featureD, txtFile;
+
+ // featureA, featureB, featureC and featureD have dependency loop
+ // problem.
+ featureA = builder.expectResource("<feature>" + "<name>featureA</name>"
+ + "<dependency>featureB</dependency>" + "<gadget>"
+ + " <script>top();gadget();</script>" + "</gadget>" + "<all>"
+ + " <script>top();all();</script>" + "</all>" + "</feature>");
+ featureB = builder.expectResource("<feature>" + "<name>featureB</name>"
+ + "<dependency>featureC</dependency>" + "<container>"
+ + " <script>mid_a();container();</script>" + "</container>"
+ + "<all>" + " <script>mid_a();all();</script>" + "</all>"
+ + "</feature>");
+ featureC = builder.expectResource("<feature>" + "<name>featureC</name>"
+ + "<dependency>featureD</dependency>" + "<container>"
+ + " <script>mid_b();container();</script>" + "</container>"
+ + "<gadget>" + " <script>mid_b();gadget();</script>"
+ + "</gadget>" + "</feature>");
+ featureD = builder.expectResource("<feature>" + "<name>featureD</name>"
+ + "<dependency>featureA</dependency>" + "<all>"
+ + " <script>bottom();all();</script>" + "</all>"
+ + "</feature>");
+ txtFile = builder.expectResource(
+ featureA.toString() + '\n' + featureB.toString() + '\n'
+ + featureC.toString() + '\n' + featureD.toString(),
+ ".txt");
+
+ registry = builder.build(txtFile.toString());
+ }
+
+ @Test(expected = GadgetException.class)
+ public void testCheckDependencyLoopWithPartialLoopDependencyError() throws
Exception {
+ TestFeatureRegistry.Builder builder = TestFeatureRegistry.newBuilder();
+ Uri featureA, featureB, featureC, featureD, txtFile;
+
+ // featureC and featureD have dependency loop problem.
+ featureA = builder.expectResource("<feature>" + "<name>featureA</name>"
+ + "<dependency>featureB</dependency>" + "<gadget>"
+ + " <script>top();gadget();</script>" + "</gadget>" + "<all>"
+ + " <script>top();all();</script>" + "</all>" + "</feature>");
+ featureB = builder.expectResource("<feature>" + "<name>featureB</name>"
+ + "<dependency>featureC</dependency>" + "<container>"
+ + " <script>mid_a();container();</script>" + "</container>"
+ + "<all>" + " <script>mid_a();all();</script>" + "</all>"
+ + "</feature>");
+ featureC = builder.expectResource("<feature>" + "<name>featureC</name>"
+ + "<dependency>featureD</dependency>" + "<container>"
+ + " <script>mid_b();container();</script>" + "</container>"
+ + "<gadget>" + " <script>mid_b();gadget();</script>"
+ + "</gadget>" + "</feature>");
+ featureD = builder.expectResource("<feature>" + "<name>featureD</name>"
+ + "<dependency>featureC</dependency>" + "<all>"
+ + " <script>bottom();all();</script>" + "</all>"
+ + "</feature>");
+ txtFile = builder.expectResource(
+ featureA.toString() + '\n' + featureB.toString() + '\n'
+ + featureC.toString() + '\n' + featureD.toString(),
+ ".txt");
+
+ registry = builder.build(txtFile.toString());
+ }
+
+ @Test
+ public void testCheckDependencyLoopWithNormalDependency() {
+ TestFeatureRegistry.Builder builder = TestFeatureRegistry.newBuilder();
+ Uri featureA, featureB, featureC, featureD, txtFile;
+
+ // There is no loop problem.
+ featureA = builder.expectResource("<feature>" + "<name>featureA</name>"
+ + "<dependency>featureB</dependency>" + "<gadget>"
+ + " <script>top();gadget();</script>" + "</gadget>" + "<all>"
+ + " <script>top();all();</script>" + "</all>" + "</feature>");
+ featureB = builder.expectResource("<feature>" + "<name>featureB</name>"
+ + "<dependency>featureC</dependency>" + "<container>"
+ + " <script>mid_a();container();</script>" + "</container>"
+ + "<all>" + " <script>mid_a();all();</script>" + "</all>"
+ + "</feature>");
+ featureC = builder.expectResource("<feature>" + "<name>featureC</name>"
+ + "<dependency>featureD</dependency>" + "<container>"
+ + " <script>mid_b();container();</script>" + "</container>"
+ + "<gadget>" + " <script>mid_b();gadget();</script>"
+ + "</gadget>" + "</feature>");
+ featureD = builder.expectResource("<feature>" + "<name>featureD</name>"
+ + "<all>" + " <script>bottom();all();</script>" + "</all>"
+ + "</feature>");
+ txtFile = builder.expectResource(
+ featureA.toString() + '\n' + featureB.toString() + '\n'
+ + featureC.toString() + '\n' + featureD.toString(),
+ ".txt");
+
+ try {
+ registry = builder.build(txtFile.toString());
+ } catch (GadgetException e) {
+ fail("Shouldn't throw a GadgetException.");
+ }
+ }
+
private GadgetContext getCtx(final RenderingContext rctx, final String
container) {
return getCtx(rctx, container, false);
}