Skip to main content
Ben Nadel at DASH by Datadog 2018 (New York, NY) with: Gabriel Zeck and JD Courtoy
Ben Nadel at DASH by Datadog 2018 (New York, NY) with: Gabriel Zeck ( @GabeZeck ) JD Courtoy ( @jd_courtoy )

Fluent API Causing StackOverflow Error During Parsing In ColdFusion 10 On Java 8

By on
Tags:

Right now, at work, we're in the process of updating our ColdFusion application servers to run on Java 8 so that we can start enforcing TLS 1.2 secure connections when making outbound CFHTTP calls. For the most part, this has been a seamless transition (up from Java 7). But, one of our ColdFusion applications starting throwing inconsistent 500 Server Errors during the application bootstrapping process. After a bit of debugging, I was able to narrow the problem down to an actual parsing error during the native compiling of the Application.cfc ColdFusion framework component.

After we introduced Java 8 to this particular ColdFusion 10 application, we started seeing StackOverflow errors in the ColdFusion logs when the application was being bootstrapped:

Apr 05, 2016 4:59:47 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [CfmServlet] in context with path [/] threw exception [Servlet execution threw an exception] with root cause
java.lang.StackOverflowError
at coldfusion.compiler.ExprAssembler.assembleExpr(ExprAssembler.java:176)
at coldfusion.compiler.ExprAssembler.cast(ExprAssembler.java:1405)
at coldfusion.compiler.StmtAssembler.cast(StmtAssembler.java:537)
at coldfusion.compiler.ExprAssembler.invoke(ExprAssembler.java:1086)
at coldfusion.compiler.ExprAssembler.assembleExpr(ExprAssembler.java:433)
at coldfusion.compiler.ExprAssembler.cast(ExprAssembler.java:1405)
at coldfusion.compiler.StmtAssembler.cast(StmtAssembler.java:537)
at coldfusion.compiler.ExprAssembler.invoke(ExprAssembler.java:1086)
at coldfusion.compiler.ExprAssembler.assembleExpr(ExprAssembler.java:433)
at coldfusion.compiler.ExprAssembler.cast(ExprAssembler.java:1405)
at coldfusion.compiler.StmtAssembler.cast(StmtAssembler.java:537)
at coldfusion.compiler.ExprAssembler.invoke(ExprAssembler.java:1086)
at coldfusion.compiler.ExprAssembler.assembleExpr(ExprAssembler.java:433)

At first, this error seemed completely useless because it didn't appear to contain any information about what was actually causing the error. Meaning, there was a StackOverflow error; but, there was no indication about what part of my code had the problematic logic.

So, I branched the code-base (using git) and started deleting large portions of the application.

Unfortunately, deleting most of the code seemed to have no effect - the error persisted across restarts of the ColdFusion service.

Luckily, I had a breakthrough when I went to CFDump out a variable and CFAbort at the top of the onApplicationStart() event handler before any of my application logic was implemented. When doing this, I was still getting the 500 Server Error. This was the key that I needed. Suddenly, I realized that it wasn't an application logic problem - ColdFusion was having trouble running the actual file. Now, the stack trace made more sense - the StackOverflow was occurring during the parsing of the Application.cfc template (which is why my CFDump / CFAbort never executed).

With an eye toward the actual structure of the CFML (ColdFusion Markup Language) syntax tree, I narrowed in on a large expression that contained a fluent API. When I deleted this large expression, the 500 Server Errors disappeared and my CFDump started showing up. I had found the culprit!

Here's a rough example of what the Application.cfc component had:

component
	output = false
	hint = "I define the application settings and event handlers."
	{

	// Define the application settings.
	this.name = hash( getCurrentTemplatePath() );
	this.applicationTimeout = createTimeSpan( 0, 0, 10, 0 );


	/**
	* I initialize the application. I get called once when the application is
	* being bootstrapped.
	*
	* @output false
	*/
	public boolean function onApplicationStart() {

		// The content of the following calls here is not that relevant. The point is
		// that it's a component that exposes a fluent API in which all of the methods
		// can be chained when configuring the component during the app bootstrapping.
		application.fluentApi = new FluentAPI()
			.withConfiguration( "someValue" )
				.doThis()
				.doThat()
				.doSomethingElse()
			.withConfiguration( "someValue" )
				.doThis()
				.doThat()
				.doSomethingElse()
			.withConfiguration( "someValue" )
				.doThis()
				.doThat()
				.doSomethingElse()
			.withConfiguration( "someValue" )
				.doThis()
				.doThat()
				.doSomethingElse()
			.withConfiguration( "someValue" )
				.doThis()
				.doThat()
				.doSomethingElse()
		;

		return( true );

	}

}

As you can see, during the application bootstrapping, I had a ColdFusion component that exposed a fluent API (ie, one that used method chaining to help the code become more readable). Apparently, this one expression was so long that it was actually causing a StackOverflow error during the native template parsing and compiling phase.

To fix this problem, I just broke the fluent expression up in to a bunch of smaller calls. Since each method in the fluent API just returned the "this" reference to the component, breaking it up was easy:

component
	output = false
	hint = "I define the application settings and event handlers."
	{

	// Define the application settings.
	this.name = hash( getCurrentTemplatePath() );
	this.applicationTimeout = createTimeSpan( 0, 0, 10, 0 );


	/**
	* I initialize the application. I get called once when the application is
	* being bootstrapped.
	*
	* @output false
	*/
	public boolean function onApplicationStart() {

		// The content of the following calls here is not that relevant. The point is
		// that it's a component that exposes a fluent API in which all of the methods
		// can be chained when configuring the component during the app bootstrapping.
		var fluentApi = application.fluentApi = new FluentAPI();

		// NOTE: We are breaking the configuration up into multiple calls in order to
		// get around a problem with the actual template parsing in Java 8 (which was
		// running into a StackOverflow error when parsing this really long expression).
		fluentApi.withConfiguration( "someValue" )
			.doThis()
			.doThat()
			.doSomethingElse()
		;
		fluentApi.withConfiguration( "someValue" )
			.doThis()
			.doThat()
			.doSomethingElse()
		;
		fluentApi.withConfiguration( "someValue" )
			.doThis()
			.doThat()
			.doSomethingElse()
		;
		fluentApi.withConfiguration( "someValue" )
			.doThis()
			.doThat()
			.doSomethingElse()
		;
		fluentApi.withConfiguration( "someValue" )
			.doThis()
			.doThat()
			.doSomethingElse()
		;

		return( true );

	}

}

This was pretty frustrating and I'm just lucky that I happen to put the right CFDump in the right place that cued me in on the actual problem. If my CFDump hadn't failed, I would never have guessed that it was an actual parsing problem caused by the size of one of my ColdFusion expressions. Especially since it was valid code and worked perfectly well on Java 6 and 7.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel