Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: Rob Dudley
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: Rob Dudley@robdudley )

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

By Ben Nadel on
Tags: ColdFusion

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.


 
 
 

 
Tweet about deleting hundreds of lines of code in order to find a stack overflow error in ColdFusion. 
 
 
 

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.



Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.