Skip to content

Instantly share code, notes, and snippets.

@isapir
Created February 23, 2012 05:09
Show Gist options
  • Save isapir/1890454 to your computer and use it in GitHub Desktop.
Save isapir/1890454 to your computer and use it in GitHub Desktop.
this component scans a folder of CFML and compares it with the /WEB-INF/railo/cfclasses to detect files that are in the source folder but were not compiled to a Java class, essentially showing old / obsolete / seldomly used cf source files
<cfcomponent output="false"
hint="this component scans a folder of CFML and compares it with the /WEB-INF/railo/cfclasses to detect files that are in the source folder but were not compiled to a Java class, essentially showing old / obsolete / seldomly used cf source files">
<!--- requires: Railo 3.x
/** Attention: this component Does Not delete any files; it merely points out
* which files were not compiled so that you can inspect them and
* decide if they can be safely removed.
*
* Warning: be sure to back up all of your source files before you make any
* chages. check source code for references of files before removing
* them in case they were not triggered by any calls but will be used
* by your system at a later time */
usage:
delete old (or all) *.class files from /WEB-INF/railo/cfclasses
let the application run for a while to allow all of the CF files to be compiled to Java class files
create a file to use this component, e.g.:
<cfset ufd = CreateObject( 'component', 'UnusedCfFilesDetector' )>
<cfset unused.Root = ufd.ScanFolderForUnusedCfFiles( '/' )>
<cfset unused.CFC = ufd.ScanFolderForUnusedCfFiles( '/cfc' )>
<cfset unused.Test = ufd.ScanFolderForUnusedCfFiles( '/test' )>
<cfdump var="#unused#">
--->
<cfset this.CFCLASSES_FOLDER = ExpandPath( '/WEB-INF/railo/cfclasses' )>
<cfset this.IgnoreList = "/|\\WEB-INF/|\\railo">
<cfset this.init()>
<cffunction name="init">
<cfset this.Mappings = getMappings()>
<cfreturn this>
</cffunction>
<cffunction name="ScanFolderForUnusedCfFiles" output="false"
hint="scans the folder at a given CF path and returns an array of all the files that don't have a corresponding Java class file">
<cfargument name="path">
<cfargument name="filter" default="*.cf?|*.cfml">
<cfargument name="recurse" default="#false#">
<cfset Local.result = []>
<cfdirectory name="Local.qFiles" action="list" directory="#ExpandPath( path )#" recurse="#recurse#" filter="#filter#">
<cfloop query="Local.qFiles">
<cfset Local.folder = resolveFolder( directory )>
<cfset Local.vpath = listFirst( this.Mappings.physical[ Local.folder ], ';' )>
<cfif right( Local.vpath, 1 ) NEQ '/'>
<cfset Local.vpath &= '/'>
</cfif>
<cfset Local.pathInfo = replace( ReplaceNoCase( directory, Local.folder, '' ), '\', '/', 'all' )>
<cfif len( Local.pathInfo )>
<cfset Local.pathInfo = mid( Local.pathInfo, 2, 256 ) & '/'>
</cfif>
<cfset Local.fullVPath = Local.vpath & Local.pathInfo & name>
<cfif !isCfclassFileExist( Local.fullVPath )>
<cfset ArrayAppend( Local.result, "#directory#\#name#" )>
</cfif>
</cfloop>
<cfreturn Local.result>
</cffunction>
<cffunction name="resolveFolder" output="false"
hint="loops through the path's parents in an attempt to find one that exists in the mapping struct">
<cfargument name="path">
<cfset Local.result = path>
<cfloop condition="#len( Local.result )#">
<cfif StructKeyExists( this.Mappings.physical, Local.result )>
<cfreturn Local.result>
</cfif>
<cfset Local.result = getParentFolder( Local.result )>
</cfloop>
</cffunction>
<cffunction name="getParentFolder" output="false"
hint="returns the parent folder of a given path, e.g. C:\folder1\folder2 from C:\folder1\folder2\folder3">
<cfargument name="path">
<cfset Local.result = path>
<cfset Local.len = len( Local.result )>
<cfloop from="#Local.len#" to="1" step="-1" index="Local.ii">
<cfif "\/" contains mid( Local.result, Local.ii, 1 )>
<cfset Local.result = left( Local.result, Local.ii - 1 )>
<cfbreak>
</cfif>
</cfloop>
<cfif Local.result IS path>
<cfset Local.result = "">
</cfif>
<cfreturn Local.result>
</cffunction>
<cffunction name="isCfclassFileExist" output="false"
hint="calls getCfclassFilePath and returns true if a file in that path exists">
<cfargument name="CfFilePath">
<cfset Local.path = getCfclassFilePath( CfFilePath )>
<cfset Local.result = FileExists( Local.path )>
<cfreturn Local.result>
</cffunction>
<cffunction name="getCfclassFilePath" output="false"
hint="takes a CF file path and returns the Java class file's path for it">
<cfargument name="CfFilePath">
<cfset Local.pc = getPageContext()>
<cfset Local.vPath = contractPath( CfFilePath )>
<cfset Local.pageSource = Local.pc.getPageSource( Local.vPath )>
<cfset Local.mapping = Local.pageSource.getMapping()>
<cfset Local.classRoot = Local.mapping.getClassRootDirectory()>
<cfset Local.result = Local.classRoot.getReal( Local.pageSource.getJavaName() )>
<cfif right( Local.result, 3 ) IS '$cf'>
<cfset Local.result &= '.class'>
</cfif>
<cfreturn Local.result>
</cffunction>
<cffunction name="getMappings" output="false"
hint="returns a struct with three sub-structs that contain the mappings defined in Railo and the Application, as well as the safe folder names in the cfclasses folder. the values are lists separated by semi-colons;">
<cfset var Local = {}>
<cfset Local.result = {}>
<cfset Local.result.virtual = {}>
<cfset Local.result.physical = {}>
<cfset Local.mappings = []>
<cfset Local.mappings = ArrayMerge( Local.mappings, GetPageContext().getServletConfig().getMappings() )>
<cfset Local.mappings = ArrayMerge( Local.mappings, GetPageContext().getServletConfig().getComponentMappings() )>
<cfset Local.mappings = ArrayMerge( Local.mappings, GetPageContext().getServletConfig().getCustomTagMappings() )>
<cfset Local.appObject = CreateObject( 'component', 'Application' )>
<cfif IsDefined( "Local.appObject.Mappings" ) && isStruct( Local.appObject.Mappings )>
<cfloop collection="#Local.appObject.Mappings#" item="Local.key">
<cfset ArrayAppend( Local.mappings, GetPageContext().getServletConfig().getApplicationMapping( Local.key, expandPath( Local.key ) ) )>
</cfloop>
</cfif>
<cfloop array="#Local.mappings#" index="Local.ai">
<cfset Local.virtual = Local.ai.virtual>
<cfset Local.physical = Local.ai.physical>
<cfif IsNumeric( mid( Local.virtual, 2, 256 ) )>
<cfset Local.virtual = '/'> <!--- custom tags folder --->
</cfif>
<cfif Local.physical contains '{'>
<cfset Local.physical = ExpandPath( Local.ai.physical )>
</cfif>
<cfif !REFindNoCase( this.IgnoreList, Local.physical )>
<!--- physical --->
<cfif StructKeyExists( Local.result.physical, Local.physical )>
<cfset Local.value = Local.result.physical[ Local.physical ]>
<cfelse>
<cfset Local.value = "">
</cfif>
<cfif !ListFind( Local.value, Local.virtual, ';' )>
<cfset Local.value = ListAppend( Local.value, Local.virtual, ';' )>
</cfif>
<cfset Local.result.physical[ Local.physical ] = Local.value>
<!--- virtual --->
<cfif StructKeyExists( Local.result.virtual, Local.virtual )>
<cfset Local.value = Local.result.virtual[ Local.virtual ]>
<cfelse>
<cfset Local.value = "">
</cfif>
<cfif !ListFind ( Local.value, Local.physical, ';' )>
<cfset Local.value = ListAppend( Local.value, Local.physical, ';' )>
</cfif>
<cfset Local.result.virtual[ Local.virtual ] = Local.value>
</cfif>
</cfloop>
<!--- cfclasses --->
<cfdirectory action="list" directory="#this.CFCLASSES_FOLDER#" name="Local.qCfClassesDir">
<cfset Local.result.cfclasses = {}>
<cfloop collection="#Local.result.physical#" item="Local.physicalPath">
<cfset Local.safeName = getSafeName( Local.physicalPath )>
<cfquery name="Local.qTemp" dbtype="query">
SELECT name
FROM Local.qCfClassesDir
WHERE name like '#Local.safeName#%'
and type = 'Dir'
</cfquery>
<cfloop query="Local.qTemp">
<cfif IsNumeric( ReplaceNoCase( Local.qTemp.name, Local.safeName, '' ) )>
<cfset Local.result.cfclasses[ Local.physicalPath ] = Local.qTemp.name>
<cfbreak>
</cfif>
</cfloop>
</cfloop>
<cfreturn Local.result>
</cffunction>
<cffunction name="getSafeName" output="false"
hint="converts a filename from something like C:\folder\file.cfm to CFC__folder_file_cfm">
<cfargument name="filename">
<cfset Local.len = len( filename )>
<cfset Local.sb = CreateObject( 'java', 'java.lang.StringBuilder' ).init( JavaCast( "int", Local.len + 2 ) )>
<cfset Local.sb.append( 'CF' )>
<cfloop from="1" to="#Local.len#" index="Local.ii">
<cfset Local.char = mid( filename, Local.ii, 1 )>
<cfif "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" contains Local.char>
<cfset Local.sb.append( Local.char )>
<cfelse>
<cfset Local.sb.append( '_' )>
</cfif>
</cfloop>
<cfreturn Local.sb.toString()>
</cffunction>
</cfcomponent>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment