tracker issue : CF-4203454

select a category, or use search below
(searches all categories and all time range)
Title:

case-sensitivity issues resolving file names in JEE deployment on Tomcat 9

| View in Tracker

Status/Resolution/Reason: To Track//PRNeedInfo

Reporter/Name(from Bugbase): Ryan Cogswell / ()

Created: 11/13/2018

Components: JEE Deployment

Versions: 13.0

Failure Type: Crash

Found In Build/Fixed In Build: 2018,0,01,311402 /

Priority/Frequency: Normal / All users will encounter

Locale/System: ALL / Win 64

Vote Count: 0

Problem Description:
Encountering a NullPointerException when ColdFusion is trying to find the location of a component when the page for the current request was requested with a different case than exists on the file system and no prior request has been made for the page with case matching the file system since the last restart of the tomcat service.

Steps to Reproduce:
Only reproducible with the JEE deployment on Tomcat 8 or 9. The standalone CF 2018 installation includes CF-specific changes to org.apache.catalina.core.StandardContext.getRealPath that prevent the error. The error does not occur on Tomcat 7.
To reproduce, create a subdirectory off of the document root and put a cfm page and a cfc in the subdirectory. The cfm should create the component using dot notation.

<!--- /mysubdir/hello.cfm --->
<cfset CreateObject("component", "mysubdir.TestComp").hello("World")>

<!--- /mysubdir/TestComp.cfc --->
<cfcomponent>
<cffunction name="hello" output="yes">
	<cfargument name="greetee">
	Hello #arguments.greetee#!
</cffunction>
</cfcomponent>

Immediately after starting the ColdFusion service, perform a request for hello.cfm using a different case than is on the file system. For example (assumes deploying cfusion.war in tomcat such that "cfusion" is the context for the CF webapp):
http://localhost:8080/cfusion/mysubdir/hELlo.cfm

Actual Result:
The following exception is produced. This will recur with additional requests until a request is made with matching case after which requests can occur with any case and still work (due to caching of the component).

java.lang.NullPointerException
	at coldfusion.util.Utils.getDirectoryFromPath(Utils.java:350)
	at coldfusion.runtime.TemplateProxyFactory.getTemplateFileHelper(TemplateProxyFactory.java:1703)
	at coldfusion.cfc.ComponentProxyFactory.getProxy(ComponentProxyFactory.java:70)
	at coldfusion.cfc.ComponentProxyFactory.getProxy(ComponentProxyFactory.java:56)
	at coldfusion.runtime.CFPage.CreateObject(CFPage.java:8057)
	at coldfusion.runtime.CFPage.CreateObject(CFPage.java:8076)
	at cfhello2ecfm1763920835.runPage(D:\apache-tomcat-9.0.13\webapps\cfusion\mysubdir\hello.cfm:1)

Expected Result:
Should work the same as when requesting with matching case (e.g. "http://localhost:8080/cfusion/mysubdir/hello.cfm") and successfully create the component.

You can see additional, relevant information by changing the contents of hello.cfm to the following:
<!--- begin hello.cfm --->
<cfset CreateObject("component", "mysubdir.TestComp").hello("World")>
<cfoutput>

<br>
<cfset cfutils = CreateObject("java", "coldfusion.util.Utils")>
getBaseTemplatePath=#cfutils.getBaseTemplatePath(getPageContext())#<br>
servletPath=#cfutils.getServletPath(getPageContext().getRequest())#<br>
ServletContext getServerInfo=#getPageContext().getServletContext().getServerInfo()#<br>
ServletContext class=#getPageContext().getServletContext().getClass().getName()#<br>
realPath of /mysubdir/hEllo.cfm=#getPageContext().getServletContext().getRealPath
("/mysubdir/hEllo.cfm")#<br>
realPath of /mysubdir/hello.cfm=#getPageContext().getServletContext().getRealPath
("/mysubdir/hello.cfm")#<br>
AppServerUtils.getAppServerType()=#CreateObject("java", 
"coldfusion.bootstrap.AppServerUtils").getAppServerType()#<br>
</cfoutput>
<!--- end hello.cfm --->

When requesting the page with lowercase, this produces:
Hello World!
getBaseTemplatePath=D:\apache-tomcat-9.0.13\webapps\cfusion\mysubdir\hello.cfm
servletPath=/mysubdir/hello.cfm
ServletContext getServerInfo=Apache Tomcat/9.0.13
ServletContext class=coldfusion.runtime.ServletContextWrapper
realPath of /mysubdir/hEllo.cfm=
realPath of /mysubdir/hello.cfm=D:\apache-tomcat-9.0.13\webapps\cfusion\mysubdir\hello.cfm
AppServerUtils.getAppServerType()=5

When requesting the page with mixed case (if a lowercase execution has occurred so that it won't error), getBaseTemplatePath will return null.

Any Workarounds:
Use Tomcat 7 (not sure if this will cause other issues, since Tomcat 9 is the "supported" version for CF 2018.
Lowercase all cfm pages in the file system and rewrite (e.g. in IIS) all cfm requests to be lowercase (this is not a practical solution for us).

Attachments:

Comments:

Case insensitive filenames result in a null pointer since Tomcat's GetRealPath's method does not return the expected absolute path.  In case of the ColdFusion standalone server, modifications to the underlying Tomcat ensure such issues are handled. We do not have control over the J2EE server when ColdFusion is deployed as a WAR, due to which such issues need to be raised with Tomcat directly.  Here's a simpler repro, The following snippet would return the expected path in case of standalone server, but not J2EE  <cfdump var="#getpagecontext().getServletContext().getRealPath('/mysubdir/hELlo.cfm')#">    
Comment by Immanuel Noel
29930 | November 14, 2018 10:18:49 AM GMT
This is not a Tomcat issue. Tomcat's getRealPath is behaving correctly in that it is supposed to be case-sensitive (even on Windows), but this was not strongly enforced in earlier Tomcat versions. ColdFusion's page and component resolution however, at least on Windows, is not supposed to be case-sensitive and for the most part it isn't. Even in the JEE deployment, even though "getRealPath('/mysubdir/hELlo.cfm')" returns null, ColdFusion successfully serves that page. It only gets the error when it tries to use the result of getRealPath to try to find a component. If you take out the creation of "mysubdir.TestComp", "/mysubdir/hELlo.cfm" works just fine. ColdFusion already works around differences in what it needs vs. the J2EE server's behavior by wrapping Tomcat's ServletContext with its own ServletContextWrapper class. In the case of component resolution, ColdFusion should have a fallback, in the case when getRealPath returns null, of leveraging whatever information it is using to correctly serve "/mysubdir/hELlo.cfm". Since JEE Deployment on Tomcat 9 is a supported platform for ColdFusion 2018, and since there is no bug in Tomcat causing this issue, a change needs to be made to ColdFusion to fix the behavior. I consider it an odd choice that the standalone version addressed this by changing its version of Tomcat, rather than addressing it in ColdFusion's wrapper which would help ensure it works in all the CF deployment options.
Comment by Ryan Cogswell
29932 | November 14, 2018 03:57:37 PM GMT