Can you compile cfc / cfm files manually in Railo 4.1?

481 views
Skip to first unread message

Bruce Kirkpatrick

unread,
Dec 2, 2013, 10:03:43 PM12/2/13
to ra...@googlegroups.com
Is it possible to compile a single CFC / CFM to Java class and control the output file/destination with CFML code?

It would be nice if we had a built in function called compileCFML(sourceCFMLFile, destinationJavaClassFile);  perhaps it could also accept compileCFML(cfmlAsString, destinationFile);

I'd like to make a custom CFML to Java deployment tools because Railo archives are inefficient (All classes are re-compiled / re-zipped on each update).

I just stopped using Railo archives for deployment.  Now, I can deploy my app in 2 seconds to remote server(s) now thanks to Rsync and rewriting how I load CFCs in application scope.  To do a sourceless version of the new system, I need to be able to compile files and handle the date/filesize logic myself.

Also, is it possible to determine the exact java class path for a specific CFC object with CFML code?

Alex Skinner

unread,
Dec 3, 2013, 9:05:44 AM12/3/13
to ra...@googlegroups.com

Would adding delta support for railo archives be an option and adding versioning support?

A

Sent from my phone

--
Did you find this reply useful? Help the Railo community and add it to the Railo Server wiki at https://github.com/getrailo/railo/wiki
---
You received this message because you are subscribed to the Google Groups "Railo" group.
To view this discussion on the web visit https://groups.google.com/d/msgid/railo/5be4d664-1cf9-439b-a85d-f2ff94b7f2ca%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

dajester2012

unread,
Dec 3, 2013, 11:39:09 AM12/3/13
to ra...@googlegroups.com
Sort of :)

I posted a thread earlier titled RailoCompilerService.  It's a webservice that provides the same functionality of cfcompile.bat (compiles a source directory to a destination directory).  It's currently an alpha release, but you can grab a WAR file at


It can run independently of any existing Railo applications, and manages the necessary mappings internally, so all you have to do is call http://localhost:8080/rcs/?sourcedir=/your/source/dir&destdir=/your/dest/dir for example...

dajester2012

unread,
Dec 3, 2013, 12:45:42 PM12/3/13
to ra...@googlegroups.com
Reading your post more thoroughly, I believe the only supported way to compile a template in Railo requires compiling or an archive of a mapping (compiling a mapping places the $cf.class files into a subdirectory of WEB-INF/railo/cfclasses instead of packaging them into a zip file).  There may be some internal java classes you could fool with if you were to look at the source for the cfadmin tag... 

If you need to do a partial compilation of your project, you could pass a sub-directory to the previously mentioned webservice as the sourcedir, and it will only compile that particular directory.  
Either that or roll your own service that 
1. copies the specific files you want to compile into a separate directory
2. add a mapping for that directory
3. compile/create an archive from that mapping
4. extract the archive and rename the files to be the same as their uncompiled counterparts
5. cleanup mappings/files

Those are similar to the steps that the webservice does for you (except #1).

Bruce Kirkpatrick

unread,
Dec 3, 2013, 2:46:39 PM12/3/13
to ra...@googlegroups.com
Alex,
I already requested "delta support" on a JIRA feature request almost a year ago.   Since that hasn't happened, I have resorted to my own solutions...

Since no one has confirmed it is possible to compile an individual cfml file to java, I've created a new feature request:

Compiling specific sub-directories sounds complicated, and still results in unnecessary duplicate work.

If I could determine the class file used by a created CFC instance, then I could do something like this to compile individual files:

p="/path/to/myCFC";
a=createobject("component", "path.to.cfc");
jp=a.getJavaClassPath();
copy=false;
if(not fileexists(p&".class")){
   copy=true;
}else{
javaFileInfo=gfileinfo(jp);
cfcFileInfo=gfileinfo(p&".cfc");
if(javaFileInfo.lastmodified NEQ cfcFileInfo.lastmodified or javaFileInfo.size NEQ cfcFileInfo.size){
copy=true;
}
}
if(copy){ 
filecopy(jp, p&".class");
}
then I could run rsync to deploy the classes to the target server excluding all cfm/cfc files so it is sourceless with the least amount of work possible.

I don't need Railo team to build a deployment system for me if I could do this.  I'm pretty sure the upcoming railo deployment feature will be incomplete or slower then this compared to what I want to do.

Bruce Kirkpatrick

unread,
Dec 3, 2013, 2:57:46 PM12/3/13
to ra...@googlegroups.com
Maybe I could do this:

Move each CFML file to a separate directory
Create a "mapping" for that directory that has only 1 file
Compile the mapping using the cfadmin tag
extract the class from the *.ra file and copy it to the same directory as the original directory with cfcName.class as the filename.

Not sure if that is faster performance.  Also I don't know if java class file are aware of what directory they were compiled in or if that is evaluated at runtime.  Might have to create the full directory tree before compiling if it does track the path used.

I'll try it later...

Jean Moniatte

unread,
Dec 3, 2013, 3:13:36 PM12/3/13
to Railo Google Group
I just wanted to say that it is really nice to have you back, Bruce. Reading you always forces me to think a little harder, outside of my little box. Thanks a lot for that.

But I keep wondering: is there gravity where you live?

:-)

Thanks,
Jean
--
Jean Moniatte
UGAL



--
Did you find this reply useful? Help the Railo community and add it to the Railo Server wiki at https://github.com/getrailo/railo/wiki
---
You received this message because you are subscribed to the Google Groups "Railo" group.

Bruce Kirkpatrick

unread,
Dec 3, 2013, 3:14:38 PM12/3/13
to ra...@googlegroups.com
Whoops, I guess you are supposed to name the java class file .cfc instead of .class

It appears that even though the class file has a string in it that says the original compilation location, it doesn't prevent you from running the class file from a different directory.   So I don't need to make a directory tree.  I'm going to post my code example of how to do this once I clean it up.   Maybe I should delete my feature request.


dajester2012

unread,
Dec 3, 2013, 5:12:21 PM12/3/13
to ra...@googlegroups.com
The only problem I see:
 
Supposing a project structure as follows:
  • /org
    • /app
      • WidgetObject.cfc
      • /base
        • Object.cfc
 where WidgetObject extends="base.Object" (ie based on a relative path). 

You cannot just copy WidgetObject by itself to say, /temp/mapping-0001 and compile it from there - the compiler would break saying it cannot find "base.Object".  In this instance, you would need to copy WidgetObject, and base/Object to the temp directory preserving the structure.  In that case, by the time you determine dependencies for the files you need to copy, I don't know that you've saved anything going this route at all.

That is why you need to pass a base directory to RailoCompilerService (same with cfcompile.bat).  BTW, I've got an update coming later that uses compileMapping instead of createArchive, which is much faster since it doesn't have to zip and unzip, plus I believe that compileMapping only compiles the files that have changed since the last time compileMapping was called (could be wrong about that part though).

Bruce Kirkpatrick

unread,
Dec 3, 2013, 6:03:01 PM12/3/13
to ra...@googlegroups.com
Jesse,

Good point about interfaces and inheritance not working.   I think I'm going to give up on this approach since it's getting complicated.

Railo doesn't execute class files unless they have the original CFM or CFC file name.   How do you deploy the class from server 1 to server 2 and have the second server use the same class files without having the source code available on server 2?   I don't see how that is possible.

I'd have to read the java source...
×Proofread

Bruce Kirkpatrick

unread,
Dec 3, 2013, 6:21:26 PM12/3/13
to ra...@googlegroups.com
I read the java source and eventually figured it out.

This is how you can generate the java class name with a mapping relative CFC/CFM path:

echo(getpagecontext().getPageSource("/compilemapping/test.cfm").getJavaName()&".class");

So with GetPageContext().compile("/mapping/test.cfm") and the above, and some other path handling code, you can do exactly what I wanted to do, yay!

I could automate copying and renaming all of the changed class files to the correct location, and rsync all the classes named as .cfm or .cfc to the remote servers for a sourceless deployment.

I believe Micha says cfdeploy tag will do the same thing, but it might not support rsync.

dajester2012

unread,
Dec 3, 2013, 6:41:05 PM12/3/13
to ra...@googlegroups.com
Glad you were able to get it working the way you wanted :)

Bruce Kirkpatrick

unread,
Dec 3, 2013, 7:04:29 PM12/3/13
to ra...@googlegroups.com
Ok, there was more complexity to java naming on the directory names and the mapping / context root java name encoding.  I had to use a different function in pagecontext to get that.   Also have to factor in root context paths vs mapping paths.    Now, I've encapsulated it all in a single simple function.  Now you can retrieve java class for a cfml path easily.   Please note I only tested on linux.

<cffunction name="getJavaClassForCFMLPath" localmode="modern"> 
<cfargument name="path" type="string" required="yes">
<cfargument name="pathIsMapping" type="boolean" required="no" default="#false#">
<cfscript>
if(arguments.pathIsMapping){
position=find("/", arguments.path, 2);
mapping=left(arguments.path, position);
}else{
mapping="/";
}
// cache to avoid the I/O overhead of expandpath
if(not structkeyexists(request, 'getJavaClassForCFMLPathCache_'&mapping)){
contextRootPath=expandpath(mapping);
contextRootPath=getpagecontext().getRelativePageSource(left(contextRootPath, len(contextRootPath)-1)).getRealPathAsVariableString();
request["getJavaClassForCFMLPathCache_"&mapping]=expandpath("/railo-context/../")&"cfclasses/"&contextRootPath;
}
return request["getJavaClassForCFMLPathCache_"&mapping]&getpagecontext().getPageSource(arguments.path).getJavaName().toString()&".class";
</cfscript>
</cffunction>
<cfscript> 
// get the absolute java class path for a mapping cfml path:
echo(getJavaClassForCFMLPath("/mapping/test.cfc", true));

// get the absolute java class path for a context root cfml path:
echo(getJavaClassForCFMLPath("/test.cfc", false));
</cfscript>

Bruce Kirkpatrick

unread,
Dec 3, 2013, 7:15:57 PM12/3/13
to ra...@googlegroups.com
That function gives you output like this:
/home/app/WEB-INF/railo/cfclasses/CF_home_app893/path/to/test_cfm225$cf.class

Those weird numbers and underscores were the problems I set out to solve with the function.

This lets me rename that long path to be /home/app/path/to/test.cfm (java code inside) when deploying to production.

Adam Cameron

unread,
Dec 3, 2013, 7:23:25 PM12/3/13
to ra...@googlegroups.com
Possibly the funniest whilst simultaneously poignant comment I've read on this forum.

Bravo.

-- 
Adam

Michael Offner

unread,
Dec 4, 2013, 3:21:19 AM12/4/13
to ra...@googlegroups.com
the tag cfdeploy coming as extension soon, will exactly support this.

Micha


2013/12/4 Bruce Kirkpatrick <br...@farbeyondcode.com>

--
Did you find this reply useful? Help the Railo community and add it to the Railo Server wiki at https://github.com/getrailo/railo/wiki
---
You received this message because you are subscribed to the Google Groups "Railo" group.

For more options, visit https://groups.google.com/groups/opt_out.



--
/micha

Michael Offner CTO Railo Technologies GmbH
Reply all
Reply to author
Forward
0 new messages