Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Andrew Dixon
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Andrew Dixon

Generate A Java Thread Dump Programmatically Using ColdFusion

By
Published in

As I've been getting used to managing an application within a larger, Kubernetes (K8) based platform, I've found myself needing to do much more programmatic introspection of the ColdFusion application and its underlying JVM (Java Virtual Machine). With such introspection, I can [hopefully] make my "liveness" and "readiness" probes smarter; and, perhaps debug deadlocks and other kind of thread contention. One type of introspection that I need to be able to do is generate a Java Thread Dump when the conditions of the ColdFusion application need to be reported.

I honestly don't know all that much about Java or the JVM. So, when it comes to generating a Java Thread Dump programmatically in ColdFusion, I am merely porting over this Crunchify article by App Shah. Shah's article generates a Java Thread Dump using Java code; so, I'm - more or less - translating that into ColdFusion code that dips down into the relevant Java functionality.

To encapsulate the complexity of the Java Thread Dump, I've created a simple ColdFusion component that exposes a .getThreadDump() method:

component
	output = false
	hint = "I help inspect the JVM threads."
	{

	/**
	* I initialize the JVM Thread Helper.
	*
	* @output false
	*/
	public any function init() {

		variables.javaManagementFactory = createObject( "java", "java.lang.management.ManagementFactory" );
		variables.threadMXBean = javaManagementFactory.getThreadMXBean();

	}

	// ---
	// PUBLIC METHODS.
	// ---

	/**
	* I return a full thread-dump of the current JVM state. This will shed light on what
	* each thread in the JVM is doing at roughly this moment.
	*
	* @maxStackDepth I determine how many stack elements should be reported for each thread.
	* @output false
	*/
	public string function getThreadDump( numeric maxStackDepth = 50 ) {

		// Gather the thread meta-data for all current JVM threads.
		// --
		// CAUTION: Since gathering the thread IDs and gathering the meta-data is a two-
		// step action, there is a chance that some of the threads (corresponding to the
		// gathered IDs) will no longer exist at the time the meta-data is gathered. In
		// such cases, the thread meta-data will be undefined in the resulting array.
		var allThreadIDs = threadMXBean.getAllThreadIds();
		var allThreads = threadMXBean.getThreadInfo( allThreadIDs, javaCast( "int", maxStackDepth ) );
		var allThreadsLength = arrayLen( allThreads );

		// Each line of the thread dump is going to be appended to this buffer. This
		// buffer will then be collapsed down into a single string.
		var buffer = [];
		// As an optimization, we can resize the buffer based on the number of threads
		// we've collected. This way, we're not continually changing the size of the
		// buffer as we iterate over the thread info.
		arrayResize( buffer, ( allThreadsLength * ( maxStackDepth + 3 ) ) );

		var newline = chr( 10 );
		var tab = chr( 9 );
		var i = 0;

		// NOTE: In later versions of ColdFusion, undefined array elements are
		// implicitly skipped in a for-in loop. But, in earlier versions of ColdFusion,
		// the iteration variable comes back as undefined.
		for ( var threadInfo in allThreads ) {

			// If the given thread had already been terminated by the time the meta-data
			// was gathered, we need to skip over it.
			if ( isNull( threadInfo ) ) {

				continue;

			}

			buffer[ ++i ] = """#threadInfo.getThreadName()#""";
			buffer[ ++i ] = "#tab#java.lang.Thread.State: #threadInfo.getThreadState().toString()#";

			for ( var element in threadInfo.getStackTrace() ) {

				buffer[ ++i ] = "#tab##tab#at #element.toString()#";

			}

			// Put a spacer in between each thread.
			buffer[ ++i ] = "";

		}

		return( trim( arrayToList( buffer, newline ) ) );

	}

}

As you can see, this code uses Java's ThreadMXBean to ask the JVM for information about all of the running threads. It then iterates over the threads and renders their state to an array, which is then collapsed down into a string.

To generate a Java Thread Dump, all we have to do is instantiate the ColdFusion component and invoke the .getThreadDump() method:

<cfscript>

	jvmThreadHelper = new JvmThreadHelper();

	writeOutput( "<pre>" & jvmThreadHelper.getThreadDump() & "</pre>" );

</cfscript>

Running this code provides output that looks like this (truncated):

A Java Thread Dump generated programmatically by ColdFusion.

I love the fact that ColdFusion is built on top of Java - it provides so much power in the underlying platform. And, hopefully this post helps other ColdFusion engineers see how they can dip down into the Java layer if they need to generate a Java Thread Dump programmatically.

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