You'll probably never need to do this, but today, I needed to find out what Java classes were available in a given JAR file using ColdFusion. The reason - I was doing some work on my POI Utility ColdFusion custom tags and I needed to see if the version of the POI JAR that ships with ColdFusion supports certain classes. Turns out, this is a fairly straightforward task. I wrapped the functionality up in a ColdFusion user defined function that takes the expanded path to the target JAR file:
<cffunction
name="GetJarClasses"
access="public"
returntype="array"
output="false"
hint="I return an array of classes in the given JAR file (expanded path).">
<!--- Define arguments. --->
<cfargument
name="JarFilePath"
type="string"
required="true"
hint="I am the expanded path of the JAR file."
/>
<!--- Define the local scope. --->
<cfset var LOCAL = {} />
<!--- Create a default array of classes. --->
<cfset LOCAL.Classes = [] />
<!---
Create a JAR input stream to read in the line items
from our target JAR file.
--->
<cfset LOCAL.JarFile = CreateObject(
"java",
"java.util.jar.JarInputStream"
).Init(
CreateObject(
"java",
"java.io.FileInputStream"
).Init(
JavaCast(
"string",
ARGUMENTS.JarFilePath
)
)
)
/>
<!---
Now that we have our JAR file input stream, let's loop
over all the entries looking for CLASS files.
--->
<cfloop condition="true">
<!---
Get the next entry. This might return NULL if the
JAR file has no more classes.
--->
<cfset LOCAL.JarEntry = LOCAL.JarFile.GetNextJarEntry() />
<!---
Check to see if the entry variable exists. If it
does not, then it means the JAR file return NULL
and we are done finding classes.
--->
<cfif StructKeyExists( LOCAL, "JarEntry" )>
<!---
Check to make sure that this entry is not a
directory, but is, in fact a class.
--->
<cfif REFindNoCase( "\.class$", LOCAL.JarEntry.GetName() )>
<!---
Add this class to the array. Since the JAR
file really has a directory structure, let's
replace the path separators with dots.
--->
<cfset LOCAL.ClassName = REReplace(
LOCAL.JarEntry.GetName(),
"[\\/]",
".",
"all"
) />
<!--- Strip off the ".class" path item. --->
<cfset LOCAL.ClassName = REReplaceNoCase(
LOCAL.ClassName,
"\.class$",
"",
"one"
) />
<!--- Add the formatted class name. --->
<cfset ArrayAppend(
LOCAL.Classes,
LOCAL.ClassName
) />
</cfif>
<cfelse>
<cfbreak />
</cfif>
</cfloop>
<!--- Return the array of classes. --->
<cfreturn LOCAL.Classes />
</cffunction>
To call it, you just do this (I had a copy of the installed POI JAR file in the same directory as my test file):
<!--- Get file path to POI installed JAR file. --->
<cfset strJarFilePath = ExpandPath(
"./poi-2.5.1-final-20040804.jar"
) />
<!--- Output the classes. --->
<cfdump
var="#GetJarClasses( strJarFilePath )#"
label="POI 2.5.1 Final Classes"
/>
Running this, we get an array with the following values (abbreviated):
org.apache.poi.ddf.DefaultEscherRecordFactory
org.apache.poi.ddf.EscherArrayProperty
org.apache.poi.ddf.EscherBSERecord
org.apache.poi.ddf.EscherBlipRecord
..... several hundred classes .....
org.apache.poi.util.ShortList
org.apache.poi.util.StringUtil
org.apache.poi.util.SystemOutLogger
Anyway, just thought I would post that in case anyone ever needs this kind of functionality.
Reader Comments
How is this different then just listing the JAR out with cfzip?
I never really play around with JAR files much, so this comment could be totally off base, but, could you have not just used the cfzip tag? It accepts a JAR file and you could use the list action and just dumped that out, right? or am i missing the point? It's Friday so my brain has shut of ffor the weekend :-)
@Todd - beat me to it :-)
@Todd, @Simon,
Oh snap, I totally forgot that CFZip could read JAR files :)
Thanks guys; amends have been made:
www.bennadel.com/index.cfm?dax=blog:1373.view