loadComponent for multiple instances?

Hi,

I am developing a Java application that uses embedded Jetty to host Lucee so that the core functionality is able to be provided by an existing CFC module.

The application calls PageContext.loadComponent(“blah”) to create an instance of said CFC. This works perfectly and methods can be called on the returned Component without issue.

However, when calling PageContext.loadComponent(“blah”) a second time, to create an instance for another company, I get this very unusual stack trace:

lucee.runtime.exp.ApplicationException: The Lucee dialect is disabled, to enable the dialect set the environment variable or system property "lucee.enable.dialect" to "true" or set the attribute "allow-lucee-dialect" to "true" with the "compiler" tag inside the lucee-server.xml.
	at lucee.runtime.PageContextImpl.notSupported(PageContextImpl.java:959)
	at lucee.runtime.component.ComponentLoader._search(ComponentLoader.java:128)
	at lucee.runtime.component.ComponentLoader._search(ComponentLoader.java:113)
	at lucee.runtime.component.ComponentLoader.searchComponent(ComponentLoader.java:75)
	at lucee.runtime.PageContextImpl.loadComponent(PageContextImpl.java:3201)
        .......

This seems to happen after the PageContext that created the first instance is released using CFMLEngine.releasePageContext(context, true).

Is this a bug? Or am I going about creating multiple instances of a Component from Java the wrong way?

I know this thread is really old, but I’m seeing the same problem.

We have some code that is running within Quartz Scheduler and we randomly see the same problem. Most of the time everything works fine, but eventually we will start seeing exception stacks like:

lucee.runtime.exp.ApplicationException: The Lucee dialect is disabled, to enable the dialect set the environment variable or system property "lucee.enable.dialect" to "true" or set the attribute "allow-lucee-dialect" to "true" with the "compiler" tag inside the lucee-server.xml.
        at lucee.runtime.PageContextImpl.notSupported(PageContextImpl.java:1081)
        at lucee.runtime.component.ComponentLoader._search(ComponentLoader.java:204)
        at lucee.runtime.component.ComponentLoader._search(ComponentLoader.java:188)
        at lucee.runtime.component.ComponentLoader.searchComponent(ComponentLoader.java:82)
        at lucee.runtime.PageContextImpl.loadComponent(PageContextImpl.java:3280)
        at lucee.runtime.op.CreationImpl.createComponentFromName(CreationImpl.java:298)
        at lucee.runtime.op.CreationImpl.createComponentFromPath(CreationImpl.java:313)

Restarting the server seems to stop them for awhile.

Any idea why this error would be getting thrown seemingly at random?

I’m guessing that somewhere in the process something is not being closed that should be closed, but I’m not sure what. Trying to replicate the behavior has been difficult because the problem generally doesn’t start until the server has been running for an extended period (i.e. weeks/months).

@micstriit,

Any ideas? Could this be because lucee.runtime.util.CreationImpl.createComponentFromPath() ends up getting null for it’s PageContext object? Could it be that the page context object is not being created properly?

It’s just weird that it normally runs fine. We’re currently on Lucee v5.4.3.2, but we’ve seen the behavior with all the previous versions of 5 we’d been on.

I’ll add that in our case once the problem starts, it’s isolated to the specific thread that originally ran into the issue. The other Quartz Scheduler threads will continue to work fine, unless they also start throwing the exception. Once the background thread starts throwing the exception, it appears to not recover until I restart Lucee (although perhaps shutting down Quartz and restarting it resolves the issue too–I’m testing that now).

@dswitzer problem most liklely is when you use a recycled PageContext, in that case the dialect is changed to Lucee, i just have changed that fact reset dialect to CFML · lucee/Lucee@8563f19 · GitHub

But the problem is most likely that you use a pageContext without initialize it. how does the code snipped that graps the PC look like?

whenever you are in a CFML request, best do simply this to get the current PC CFMLEngineFactory.getInstance().getThreadPageContext()

1 Like

@micstriit,

Thanks for taking the time to provide feedback!

So today, it tries two ways to create the page context. It first tries:

lucee.loader.engine.CFMLEngine.getThreadPageContext()

But this seems to only be available when running within an actual Lucee page request. This ends up working if I’m manually firing off a task from within the Quartz Scheduler, but when it’s running within a Quartz Scheduler background thread, this ends up returning null. When this happens, I call the following methods:

public PageContext createPageContext(boolean register) throws Throwable, ServletException {
	final CFMLEngine engine = CFMLEngineFactory.getInstance(this.servletConfig);
	long timeout = -1;

	PageContext pc = engine.createPageContext(
		  this.rootFile// webroot
		, this.httpHost // HOST, e.g. "localhost.com"
		, "/" // SCRIPT_NAME, e.g. "/websockets/test.cfm"
		, "" // QUERY_STRING
		, new Cookie[0] // Cookies, cfid and cftoken are required for to retrieve Session
		, null // headers, can also be null
		, null // parameters
		, null // attributes
		, DevNullOutputStream.DEV_NULL_OUTPUT_STREAM // response stream where the output is written to
		, timeout // timeout for the simulated request in milli seconds
		, register // register the pc to the thread
	);

	if( this.applicationContext != null ){
		pc.setApplicationContext(this.applicationContext);
	}

	return pc;		
}

The properties used, are then initialize in the constructor:

public LuceeApp(PageContext pc) {
	this.servletConfig = pc.getServletConfig();
	this.servletContext = pc.getServletContext();
	this.webConfig = pc.getConfig();
	this.applicationContext = pc.getApplicationContext();
	this.rootDir = this.servletContext.getRealPath("/");
	this.rootFile = new File(this.rootDir);

	try {
		this.httpHost = (String) pc.cgiScope().get(engine.getCastUtil().toKey("HTTP_HOST"));
	} catch (PageException ex) {
		this.httpHost = "localhost";
	}
}

When my Lucee web app initializing, this object is created and stored as a singleton and my background threads end up using this cached object to generate it’s page context.

Given your response, I’m sure that’s where the problem lies.

Well CFMLEngineFactory.getInstance().getThreadPageContext() work even when run from outside of a Lucee web request?

It’s been a while since I wrote this code, but the problem I was running into was getting a page context object that referenced the application I needed.

What would be the proper way to get a page context object in this scenario?

Thanks!
-Dan

@micstriit,

So I’ve confirmed the following ends up being null when running inside a Quartz scheduler. If that’s null, what would be the correct way to initialize it?

@micstriit,

Would implementing it like CLIInvokerImpl be the right approach?

public CLIInvokerImpl(final File root, final String servletName) throws ServletException {

	final Map<String, Object> attributes = new HashMap<String, Object>();
	final Map<String, String> initParams = new HashMap<String, String>();

	final String param = Util._getSystemPropOrEnvVar("lucee.cli.config", null);

	if (param != null && !param.isEmpty()) {

		initParams.put("lucee-web-directory", new File(param, "lucee-web").getAbsolutePath());
		initParams.put("lucee-server-directory", new File(param).getAbsolutePath()); // will create a subfolder named lucee-server
	}
	else initParams.put("lucee-server-directory", new File(root, "WEB-INF").getAbsolutePath());

	final ServletContextImpl servletContext = new ServletContextImpl(root, attributes, initParams, 1, 0);
	servletConfig = new ServletConfigImpl(servletContext, servletName);
	engine = CFMLEngineFactory.getInstance(servletConfig);
	servletContext.setLogger(engine.getCFMLEngineFactory().getLogger());
}

Or is lucee.runtime.engine.JavaProxyUtilImpl safe to use? I couldn’t find any usage of it in the code (but I might be missing something). It looks like maybe that would ultimately be safer to use the code I’m using.