Skip to content
Browse files

If include statements from a cycle, throw a nicer error

  • Loading branch information...
1 parent 36f4e2e commit 2dc420ccf0d0ed17636b3f16cdcaac700bade394 @havocp havocp committed Apr 9, 2012
View
2 NEWS.md
@@ -30,6 +30,8 @@
implement ConfigIncluderFile, ConfigIncluderURL, and
ConfigIncluderClasspath. You should also use
ConfigIncludeContext.parseOptions() if appropriate.
+ - cycles in include statements (self-includes) are now detected
+ and result in a nicer error instead of stack overflow
- the serialization format has changed for a Config that has not
had resolve() called on it. The library can still deserialize
the old format, but old versions of the library will not be
View
34 config/src/main/java/com/typesafe/config/impl/Parseable.java
@@ -20,6 +20,7 @@
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.Properties;
import com.typesafe.config.ConfigException;
@@ -43,8 +44,16 @@
private ConfigParseOptions initialOptions;
private ConfigOrigin initialOrigin;
- protected Parseable() {
+ private static final ThreadLocal<LinkedList<Parseable>> parseStack = new ThreadLocal<LinkedList<Parseable>>() {
+ @Override
+ protected LinkedList<Parseable> initialValue() {
+ return new LinkedList<Parseable>();
+ }
+ };
+
+ private static final int MAX_INCLUDE_DEPTH = 50;
+ protected Parseable() {
}
private ConfigParseOptions fixupOptions(ConfigParseOptions baseOptions) {
@@ -122,7 +131,23 @@ static AbstractConfigObject forceParsedToObject(ConfigValue value) {
@Override
public ConfigObject parse(ConfigParseOptions baseOptions) {
- return forceParsedToObject(parseValue(baseOptions));
+
+ LinkedList<Parseable> stack = parseStack.get();
+ if (stack.size() >= MAX_INCLUDE_DEPTH) {
+ throw new ConfigException.Parse(initialOrigin, "include statements nested more than "
+ + MAX_INCLUDE_DEPTH
+ + " times, you probably have a cycle in your includes. Trace: " + stack);
+ }
+
+ stack.addFirst(this);
+ try {
+ return forceParsedToObject(parseValue(baseOptions));
+ } finally {
+ stack.removeFirst();
+ if (stack.isEmpty()) {
+ parseStack.remove();
+ }
+ }
}
final AbstractConfigValue parseValue(ConfigParseOptions baseOptions) {
@@ -350,6 +375,11 @@ protected Reader reader() {
protected ConfigOrigin createOrigin() {
return SimpleConfigOrigin.newSimple("String");
}
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(" + input + ")";
+ }
}
public static Parseable newString(String input, ConfigParseOptions options) {
View
1 config/src/test/resources/cycle.conf
@@ -0,0 +1 @@
+include "cycle.conf"
View
9 config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
@@ -817,4 +817,13 @@ class PublicApiTest extends TestUtils {
assertTrue("cache was dropped when switching loaders", load3 ne load7)
assertEquals(load3, load7)
}
+
+ @Test
+ def detectIncludeCycle() {
+ val e = intercept[ConfigException.Parse] {
+ ConfigFactory.load("cycle")
+ }
+
+ assertTrue("wrong exception: " + e.getMessage, e.getMessage.contains("include statements nested"))
+ }
}

0 comments on commit 2dc420c

Please sign in to comment.
Something went wrong with that request. Please try again.