If include statements from a cycle, throw a nicer error

This commit is contained in:
Havoc Pennington 2012-04-09 19:18:46 -04:00
parent 36f4e2e61a
commit 2dc420ccf0
4 changed files with 44 additions and 2 deletions

View File

@ -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 File

@ -20,6 +20,7 @@ import java.net.URISyntaxException;
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 @@ public abstract class Parseable implements ConfigParseable {
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 @@ public abstract class Parseable implements ConfigParseable {
@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 @@ public abstract class Parseable implements ConfigParseable {
protected ConfigOrigin createOrigin() {
return SimpleConfigOrigin.newSimple("String");
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + input + ")";
}
}
public static Parseable newString(String input, ConfigParseOptions options) {

View File

@ -0,0 +1 @@
include "cycle.conf"

View File

@ -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"))
}
}