Using Hibernate with ColdFusion, part 2
In first part of this article we have built first version of HibernateProxy.cfc which creates Hibernate SessionFactory and returns Session instance. In this part we will create JavaBuilder.cfc. This component is responsible for creating Java source files, compiling and loading them.
First we should focus on JavaBuilder.cfc. Below you can see JavaBuilder.cfc source code. Take a look at it and we will discuss it soon.
<cfcomponent>
<cffunction name="build" access="public" output="false" returntype="any">
<cfargument name="proxy" type="HibernateProxy" required="true" />
<cfargument name="entity" type="WEB-INF.cftags.component" required="true" />
<cfargument name="name" type="string" required="true" />
<cfargument name="path" type="string" required="true" />
<cfset var packageName = listDeleteAt(
arguments.name,listLen(arguments.name,"."),
".") />
<cfset var _classStart = "" />
<cfset var _class = "" />
<cfset var _imports = structNew() />
<cfset var i = 0 />
<cfset var props = arrayNew(1) />
<cfset var matchingType = "" />
<cfset var key = "" />
<cfif hasXMLDefinition( listLast(arguments.name,"."), arguments.path )>
<cfscript>
// add serializable to imports:
_imports["java.io.Serializable"] = "java.io.Serializable";
// get all properties defined by <cfproperty> tags:
// TODO: Properties should be tested against mapping files:
props = findProperties( arguments.entity );
// build package name:
_classStart = "package " & packageName & ";" & chr(10) & chr(10);
// build class definition - always implement Serializable:
_class = _class & "public class "
& listLast(arguments.name,".")
& " implements Serializable {" & chr(10);
// generate properties with getters and setters:
for (i=1; i lte arrayLen(props); i=i+1)
{
matchingType = findMatchingType( props[i].TYPE );
type = "";
if ( matchingType eq "__struct__" or matchingType eq "__array__" )
{
if ( matchingType eq "__struct__" )
{
_imports["java.util.Hashtable"] = "java.util.Hashtable";
type = "Hashtable";
}
else if ( matchingType eq "__array__" )
{
type = "Object[]";
}
}
else
{
_imports[matchingType] = matchingType;
type = listLast(matchingType,".");
}
// property:
_class = _class & " private " & type & " " & props[i].NAME
& " = null;" & chr(10);
// getter:
_class = _class & " public " & type & " get" & props[i].NAME
& "() {" & chr(10);
_class = _class & " return this." & props[i].NAME & ";" & chr(10);
_class = _class & " }" & chr(10);
// setter:
_class = _class & " public void set" & props[i].NAME & "("
& type & " value) {" & chr(10);
_class = _class & " this." & props[i].NAME & " = value;" & chr(10);
_class = _class & " }" & chr(10);
}
_class = _class & "}" & chr(10);
// add imports:
for (key in _imports)
_classStart = _classStart & "import " & _imports[key] & ";" & chr(10);
_classStart = _classStart & chr(10);
// get full class as string:
_class = _classStart & _class;
// save .java file:
doSaveJavaSource( getJavaSourcePath(
listLast(arguments.name,"."), arguments.path ), _class );
// compile source using internal compiler:
doCompile(
getCompilerOutPath( arguments.proxy, arguments.name ),
getJavaSourcePath( listLast(arguments.name,"."), arguments.path ) );
if ( arguments.proxy.isDeleteJavaSource() )
// delete .java file:
doDeleteJavaSource(
getJavaSourcePath( listLast(arguments.name,"."), arguments.path ) );
// create class loader:
loader = createObject("java",
"uk.co.riait.cf.hibernate.HiberCFClassLoader").init(
getPageContext().getClass().getClassLoader() );
// root directory is like CLASSPATH for saved classes:
loader.setClassLoaderRootDirectory( arguments.proxy.getApplicationRoot() );
// load class and return it;
// could throw ClassNotFoundException:
return loader.loadClass( arguments.name, javaCast("boolean",true) );
</cfscript>
<cfelse>
<cfthrow type="JavaBuilder.Build"
message="Could not handle #arguments.name# because it has
no matching XML definition." />
</cfif>
</cffunction>
<cffunction name="findMatchingType" access="private"
output="false" returntype="string">
<cfargument name="type" type="string" required="true" />
<cfscript>
var _types = structNew();
_types.string = "java.lang.String";
_types.binary = "java.lang.String"; // not sure of this one now...
_types.date = "java.util.Date";
_types.numeric = "java.lang.Double";
_types.boolean = "java.lang.Boolean";
_types.struct = "__struct__";
_types.array = "__array__";
if ( structKeyExists( _types, arguments.type ) )
return _types[arguments.type];
return arguments.type;
</cfscript>
</cffunction>
<cffunction name="hasXMLDefinition" access="private"
output="false" returntype="boolean">
<cfargument name="name" type="string" required="true" />
<cfargument name="path" type="string" required="true" />
<cfreturn fileExists( replace(
arguments.path, arguments.name&".cfc",
arguments.name&".hbm.xml") ) />
</cffunction>
<cffunction name="getJavaSourcePath" access="private"
output="false" returntype="string">
<cfargument name="name" type="string" required="true" />
<cfargument name="path" type="string" required="true" />
<cfreturn replace(
arguments.path, arguments.name&".cfc", arguments.name&".java") />
</cffunction>
<cffunction name="getCompilerOutPath" access="private"
output="false" returntype="string">
<cfargument name="proxy" type="HibernateProxy" required="true" />
<cfargument name="name" type="string" required="true" />
<cfreturn replace(arguments.proxy.getApplicationRoot(),'//','/','all') />
</cffunction>
<cffunction name="findProperties" access="private"
returntype="array" output="false">
<cfargument name="entity" type="WEB-INF.cftags.component" required="true" />
<cfscript>
var p = getMetaData(arguments.entity);
var properties = arrayNew(1);
var tmpProperties = arrayNew(1);
while (true)
{
tmpProperties = arrayNew(1);
if (structKeyExists(p, "properties"))
tmpProperties = p.properties;
for (i=1; i lte arrayLen(tmpProperties); i=i+1)
arrayAppend(properties,tmpProperties[i]);
if (structKeyExists(p, "extends"))
p = p.extends;
else
break;
}
return properties;
</cfscript>
</cffunction>
<cffunction name="doSaveJavaSource" access="private"
output="false" returntype="void">
<cfargument name="path" type="string" required="true" />
<cfargument name="source" type="string" required="true" />
<cffile
action="write"
file="#arguments.path#"
output="#arguments.source#"
charset="utf-8"
nameconflict="overwrite" />
</cffunction>
<cffunction name="doDeleteJavaSource" access="private"
output="false" returntype="void">
<cfargument name="path" type="string" required="true" />
<cffile
action="delete"
file="#arguments.path#" />
</cffunction>
<cffunction name="doCompile" access="private" output="false" returntype="void">
<cfargument name="outputPath" type="string" required="true" />
<cfargument name="sourcePath" type="string" required="true" />
<cfscript>
var compiler = createObject("java", "jrunx.compiler.JavaCompiler").init();
var f1 = createObject("java", "java.io.File").init(arguments.sourcePath);
var f2 = createObject("java", "java.io.File").init(arguments.outputPath);
compiler.compile(f1,f2,f2);
</cfscript>
</cffunction>
</cfcomponent>
First and most important method here is build(). This method accepts following arguments: HibernateProxy instance, CFC instance (entity), CFC name and CFC path. It first checks for matching HBM XML file. This file must have exactly the same name as CFC and must be placed in the same directory as CFC. If HBM file was found build() method fetches all properties of CFC (defined with <cfproperty> tags) by looking at meta data recursively. Next it iterates over properties and builds Java source file. It is doing it by simply building string which is later on saved as .java file. While iterating private fields are written along with set and get methods. When source file is saved compile() method is executed. It uses JRun built-in JavaCompiler. This class is located in jrunx.compiler package (package is located in {CFUSION_INSTALL_DIR}/runtime/lib/jrun.jar file). Once class is compiled it is then loaded by custom ClassLoader. HiberCFClassLoader is the variation of JavaLoader created by Mark Mandel. Original project is available here.
Here is the source code of HiberCFClassLoader class:
package uk.co.riait.cf.hibernate;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Hashtable;
public class HiberCFClassLoader extends ClassLoader {
private ClassLoader parent = null;
private String root = null;
private Hashtable classCache = new Hashtable();
public HiberCFClassLoader() {
}
public HiberCFClassLoader(ClassLoader parent) {
setParent(parent);
}
protected final void setParent(ClassLoader parent) {
this.parent = parent;
}
public void setClassLoaderRootDirectory( String dir ) {
this.root = dir;
}
private byte[] loadClassBytes(String resource) {
try {
File f = new File( resource );
return readFully(f.toURI().toURL().openStream());
}
catch(Exception e) {
return null;
}
}
private byte[] readFully(InputStream is) throws IOException {
byte[] buf = new byte[1024];
int num = 0;
ByteArrayOutputStream bout = new ByteArrayOutputStream();
while( (num = is.read(buf)) != -1)
bout.write(buf, 0, num);
return bout.toByteArray();
}
protected byte[] loadClassData(String classname) {
String resourceName = classname.replace('.', '/') + ".class";
return loadClassBytes(resourceName);
}
protected Class defineClass(String classname, byte[] classdata) {
return defineClass(classname, classdata, 0, classdata.length);
}
public synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class c = null;
try {
c = findSystemClass(name);
}
catch(ClassNotFoundException cnfe) {
}
finally {
if (c != null) return c;
}
try {
if (parent != null) c = parent.loadClass(name);
}
catch(ClassNotFoundException cnfe) {
}
finally {
if (c != null) return c;
}
c = (Class) classCache.get(name);
if(c != null) return c;
byte[] data = loadClassData(name);
if(data != null) {
c = defineClass(name, data);
classCache.put(name, c);
if(resolve)
resolveClass(c);
}
else {
throw new ClassNotFoundException(name);
}
return c;
}
}
When class is loaded it is returned to HibernateProxy.cfc.
Third part of this article covers loading class from HibernateProxy.cfc and caching it.
About this entry
You’re currently reading “ Using Hibernate with ColdFusion, part 2 ,” an entry on ria:it
- Published:
- 6:48 pm on 25/05/2008
- Category:
- ColdFusion, Hibernate
No comments
Jump to comment form | comments rss [?] | trackback uri [?]