[Coldbox 6.6.1] Permission denied for creating Java object: coldfusion.sql.DataSrcImpl

For security reasons, our CF server has the “Disable access to internal ColdFusion Java components” checked. This however creates an issue for CB6, because it’ll error out trying to access “coldfusion.sql.DataSrcImpl” and “coldfusion.filter.FusionContext” in system\async\proxies\BaseProxy.cfc.

Is it possible to achieve the same goal without needing to access any internal CF Java components? Or is it safe to simply comment them out, and thus removing these two lines in “unLoadContext()”?
variables.DataSrcImplStatic.clearSqlProxy();
variables.fusionContextStatic.setCurrent( javacast( null, ) );

1 Like

We have this issue as well.

This is needed if any async programming will be done with ColdBox. If you won’t be doing any usage of our async packages then yes you can ignore it. @bdw429s can comment further.

The DataSrcImpl class is used to release any used DB connections from inside of the thread. Without it, if you are hitting the DB from inside of an async thread, those connections will never be released and your number of open connections will grow forever.

The FusionContext class is used to bring the current application scope into the thread (CFMappings, Application.cfc settings like datasources, etc, and the application scope itself.

No, sadly Adobe CF provides no other way to do this. I’ve been asking the engineers for a feature like this for years. I did recently put in this ticket: Tracker

If you don’t ever have any CFQueries, queryexecute(), etc inside of ColdBox async threads, then it’s safe to remove the DataSrcImpl line, but I’m pretty sure stuff won’t work without the fusion context in place. Feel free to try it, but you’re on your own unfortunately. Better yet, talk your hosting company into making an exception so ColdBox can run properly.

We’re definitely not doing any async programming. The error comes from ColdBox itself trying to init async.proxies.Runnable, which extends async.proxies.BaseProxy (I’ll post the stack trace if anyone’s interested - it’s something to do with CacheBox as far as I can tell), although I don’t think either DataSrcImpl or FusionContext are ever used after it’s instantiated.
I ended up refactoring BaseProxy.cfc to “lazyload” coldfusion.sql.DataSrcImpl and coldfusion.filter.FusionContext, and so far everything seems to work without errors.

Can you post your code to see if we can add this to the core

@lmajano here’s the code with the “lazyloading”…

/**
 * Functional interface base dynamically compiled via dynamic proxy
 */
component accessors="true" {

	/**
	 * java.lang.System
	 */
	property name="System";

	/**
	 * java.lang.Thread
	 */
	property name="Thread";

	/**
	 * Debug Mode or not
	 */
	property name="debug" type="boolean";

	/**
	 * Are we loading the CFML app context or not, default is true
	 */
	property name="loadAppContext" type="boolean";

	/**
	 * The target function to be applied via dynamic proxy to the required Java interface(s)
	 */
	property name="target";

	/**
	 * Constructor
	 *
	 * @target         The target function to be applied via dynamic proxy to the required Java interface(s)
	 * @debug          Add debugging messages for monitoring
	 * @loadAppContext By default, we load the Application context into the running thread. If you don't need it, then don't load it.
	 */
	function init(
		required target,
		boolean debug          = false,
		boolean loadAppContext = true
	){
		variables.System         = createObject( "java", "java.lang.System" );
		variables.Thread         = createObject( "java", "java.lang.Thread" );
		variables.debug          = arguments.debug;
		variables.target         = arguments.target;
		variables.UUID           = createUUID();
		variables.loadAppContext = arguments.loadAppContext;

		// If loading App context or not
		if ( arguments.loadAppContext ) {
			if ( server.keyExists( "lucee" ) ) {
				variables.cfContext   = getCFMLContext().getApplicationContext();
				variables.pageContext = getCFMLContext();
			} else {
				variables.productVersion        = listFirst( server.coldfusion.productVersion );
				variables.originalPageContext   = getCFMLContext();
				variables.originalPage          = variables.originalPageContext.getPage();
			}
			// out( "==> Storing contexts for thread: #getCurrentThread().toString()#." );
		}

		return this;
	}

	/**
	 * Get the current thread java object
	 */
	function getCurrentThread(){
		return variables.Thread.currentThread();
	}

	/**
	 * Get the current thread name
	 */
	function getThreadName(){
		return getCurrentThread().getName();
	}

	/**
	 * This function is used for the engine to compile the page context bif into the page scope,
	 * if not, we don't get access to it.
	 */
	function getCFMLContext(){
		return getPageContext();
	}

	/**
	 * Ability to load the context into the running thread
	 */
	function loadContext(){
		// Are we loading the context or not?
		if ( !variables.loadAppContext ) {
			return;
		}

		// out( "==> Context NOT loaded for thread: #getCurrentThread().toString()# loading it..." );

		try {
			// Lucee vs Adobe Implementations
			if ( server.keyExists( "lucee" ) ) {
				getCFMLContext().setApplicationContext( variables.cfContext );
			} else {
				// Set the current thread's class loader from the CF space to avoid
				// No class defined issues in thread land.
				getCurrentThread().setContextClassLoader(
					getOriginalFusionContext().getClass().getClassLoader()
				);

				// Prepare a new context in ACF for the thread
				var fusionContext = getOriginalFusionContext().clone();
				getFusionContextStatic().setCurrent( fusionContext );
				// Create a new page context for the thread
				var pageContext = variables.originalPageContext.clone();
				// Reset it's scopes, else bad things happen
				pageContext.resetLocalScopes();
				// Set the cf context into it
				pageContext.setFusionContext( fusionContext );
				fusionContext.pageContext = pageContext;
				if ( !isNull( getOriginalAppScope() ) ) {
					fusionContext.SymTab_setApplicationScope( getOriginalAppScope() );
				}

				// Create a fake page to run this thread in and link it to the fake page context and fusion context
				var page             = variables.originalPage._clone();
				page.pageContext     = pageContext;
				page.pageContext     = pageContext;
				fusionContext.parent = page;

				// Set the current context of execution now
				pageContext.initializeWith(
					page,
					pageContext,
					pageContext.getVariableScope()
				);
			}
		} catch ( any e ) {
			err( "Error loading context #e.toString()#" );
			writeDump(
				var    = [ createObject( "java", "coldfusion.filter.FusionContext" ).getCurrent() ],
				output = "console",
				label  = "FusionContext Exception - Get Current",
				top    = 5
			);
		}
	}

	/**
	 * Ability to unload the context out of the running thread
	 */
	function unLoadContext(){
		// Are we loading the context or not?
		if ( !variables.loadAppContext ) {
			return;
		}

		// out( "==> Removing context for thread: #getCurrentThread().toString()#." );

		try {
			// Lucee vs Adobe Implementations
			if ( server.keyExists( "lucee" ) ) {
			} else {
				// Ensure any DB connections used get returned to the connection pool. Without clearSqlProxy an executor will hold onto any connections it touched while running and they will not timeout/close, and no other code can use the connection except for the executor that last touched it.   Credit to Brad Wood for finding this!
				getDataSrcImplStatic().clearSqlProxy();
				getFusionContextStatic().setCurrent( javacast( "null", "" ) );
			}
		} catch ( any e ) {
			err( "Error Unloading context #e.toString()#" );
		}
	}

	/**
	 * Utility to send to output to console from a runnable
	 *
	 * @var Variable/Message to send
	 */
	function out( required var ){
		variables.System.out.println( arguments.var.toString() );
	}

	/**
	 * Utility to send to output to console from a runnable via the error stream
	 *
	 * @var Variable/Message to send
	 */
	function err( required var ){
		variables.System.err.println( arguments.var.toString() );
	}

	/**
	 * Engine-specific lock name. For Adobe, lock is shared for this CFC instance.  On Lucee, it is random (i.e. not locked).
	 * This singlethreading on Adobe is to workaround a thread safety issue in the PageContext that needs fixed.
	 * Amend this check once Adobe fixes this in a later update
	 */
	function getConcurrentEngineLockName(){
		if ( server.keyExists( "lucee" ) ) {
			return createUUID();
		} else {
			return variables.UUID;
		}
	}

	function getDataSrcImplStatic() {
		if (not isDefined("variables.dataSrcImplStatic")) {
			variables.dataSrcImplStatic = createObject( "java", "coldfusion.sql.DataSrcImpl" );
		}

		return variables.dataSrcImplStatic;
	}

	function getFusionContextStatic() {
		if (not isDefined("variables.fusionContextStatic")) {
			variables.fusionContextStatic = createObject( "java", "coldfusion.filter.FusionContext" );
		}

		return variables.fusionContextStatic;
	}

	function getOriginalFusionContext() {
		if (not isDefined("variables.originalFusionContext")) {
			variables.originalFusionContext = getFusionContextStatic().getCurrent().clone();
		}

		return variables.originalFusionContext;
	}

	function getOriginalAppScope() {
		if (not isDefined("variables.originalAppScope")) {
			variables.originalAppScope = getOriginalFusionContext().getAppHelper().getAppScope();
		}

		return variables.originalAppScope;
	}
}

@nickwong I don’t think that method will actually work. Unless Adobe doesn’t check the creation of Java classes inside of a thread, it’s still going to error when the createObject() runs, the error may just get lost in the void, and any datasource connections opened inside the thread will not the returned to the connection pool.

Furthermore, the FusionContextStatic.getCurrent() logic MUST run inside of the original CF thread that spawns the async proxy or it won’t actually be getting the correct current fusioncontext. If you “lazy load” it later so it runs once inside of the thread executor pool, it will be too late.

Now, it’s entirely possible the specific code CacheBox runs inside of the threads doesn’t noticed that the fusioncontext is incorrect, and if so, maybe we can make ColdBox fall back better for those threads, but your “solution” wouldn’t allow for proper use of the async functionality for other custom purposes.