JavaLoader 1.0
JavaLoader is a library that has been built to ease the use,
development and integration of Java within ColdFusion applications.
It has several key features:
- Dynamically loading Java libraries without having to place them on the ColdFusion classpath
- Dynamically compiling Java code to be utilised within ColdFusion Applications
- Providing
a Java Dynamic Proxy for ColdFusion Components to allow for seamless
interoperability between Java and ColdFusion Components
- Spring integration with ColdFusion Components
- and more...
Installation The easiest way to install the javaloader, either put the
javaloader in the root of your web application, or make a mapping
called 'javaloader' to the javaloader folder.
Examples
Usage examples are provided in the download zip in the folder /examples
Class Loading
This is the core function of JavaLoader. To take a Java Library that is
stored on your file system, and make it available for you to use in
your ColdFusion application without having to load it into the
ColdFusion class path.
Requirements
- ColdFusion 7, 8 or 9
- Java 1.4+
API Documentation
ColdDoc DocumentationNetworkClassLoader JavaDocReference
To use JavaLoader to load Class that are stored in a file path, you can use it like so:
createObject("component", "javaloader.JavaLoader").init(loadPaths [,loadColdFusionClassPath] [,parentClassLoader]);
There are three arguments for classloading that are possible to use to configure how and what the JavaLoader loads.
parameter:
loadPaths An array of directories of classes, or paths to .jar files to load.
An example would be:
loadPaths = ArrayNew(1);
loadPaths[1] = expandPath("icu4j.jar");
loadPaths[2] = expandPath("log4j.jar");
parameter:
loadColdFusionClassPath (default: false)
Loads the ColdFusion libraries with the loaded libraries.
This used to be on by default, however now you must implicitly set it
to be true if you wish to access any of the libraries that ColdFusion
loads at application start up.
parameter:
parentClassLoader (default: null)
(Expert use only) The parent java.lang.ClassLoader to set when creating the URLClassLoader.
Note - when setting loadColdFusionClassPath to 'true', this value is overwritten with the ColdFusion classloader.
To create an instance of a Java Class, you then only need to call:
javaloader.create(className).init(arg1, arg2...);
parameter:
className The name of the Java Class to create.
This works exactly the same as
createObject("java", className), such that simply calling
create(className) gives you access to the static properties of the class, but to get an instance through calling the
Constructor you are required to call
create(className).init();
For example:
javaloader.create("org.apache.log4j.Logger").init("my log");
Dynamic Compilation
To
ease the pain of compiling and archiving your Java classes into .jar
files to be used by JavaLoader, JavaLoader now provides the ability to
dynamically compile and load your Java source code.
Java 1.6+ is required for dynamic compilation, as it leverages the Java 1.6 Compiler API.
Requirements
- ColdFusion 8 or 9
- Java 1.6+
API Documentation
ColdDoc DocumentationReference
To use JavaLoader to load Classes that are stored in a file path, and
dynamically compile your Java source, you can use it like so:
createObject("component", "javaloader.JavaLoader").init([loadPaths] [,loadColdFusionClassPath] [,parentClassLoader] [,sourceDirectories] [,compileDirectory] [,trustedSource]);
There are six arguments for classloading that are possible to use to configure how and what JavaLoader loads and compiles.
parameter:
loadPaths
If you are compiling Java code that have dependencies on other
libraries, include them with this argument. For more details on
loadPaths, see
Class Loading parameter:
loadColdFusionClassPath (default: false)
This parameter has no effect on dynamic compilation, but may be useful for
Class Loading.
parameter:
parentClassLoader (default: null)
This parameter has no effect on dynamic compilation, but may be useful for
Class Loading.
parameter:
sourceDirectories (default: arrayNew(1)) This is an array of directories that you wish to be compiled and loaded by JavaLoader.
It should be noted that JavaLoader will load
all
the files in the provided source directories, which is very useful if
your Java code needs to find resource files on the classpath.
parameter:
compileDirectory (default: ./tmp folder with in /javaloader)
This is the folder that JavaLoader creates the .jar file from the
source code dynamically. You will only need to change this if
there is an issue writing to that directory on your system.
parameter:
trustedSource (default: false)
By default, JavaLoader will check to see if any of your source has
changed, and recompile the source code before returning you an object on
create(). On production systems, it is useful to turn this off for performance reasons by setting
trustedSource to 'true'.
It is also worth noting that when
trustedSource
is 'true', JavaLoader will retain the compiled JAR file, and reuse it
on Server restarts, mitigating the server startup performance cost of
recompilation.
If you have set up your dynamic compilcation
correctly, then you can simply instantiate the Java objects that have
been compiled (or loaded) in the same way as you would in
Class Loading.
ColdFusion Component Dynamic Proxy
The ColdFusion component dynamic proxy is Java library that provides a
Java Dynamic Proxy
that allows for the ability to pass ColdFusion Components to Java
objects and have Java objects call the CFC methods seamlessly.
Requirements
- ColdFusion 8 or 9
- Java 1.5+
- Including the following .jar's with the JavaLoader loadpaths:
- /javaloader/support/cfcdynamicproxy/lib/cfcdynamicproxy.jar
- loadColdFusionClassPath must be true
Java Build Path Requirements
If
you are using the Dynamic Proxy within a Java Project, the following
libraries will need to be added to your Build Path so that it can
compile:
- /<coldfusion-home>/lib/cfusion.jar
If you need access to any of the J2EE classes, you can import them from:
- /<coldfusion-home>/runtime/lib/jrun.jar
API Documentation
JavaDoc DocumentatonReference
The ColdFusion Component Dynamic Proxy is able to be wrapped around a CFC, and thereby make Java Objects
think
they are interacting with a native Java object, which implements a
given set of interfaces, but in fact, communicate directly with your
CFC.
When a Java Object calls a method on the ColdFusion
Component Dynamic Proxy, it is passed through and invoked directly on
the CFC, completely transparent to your Java layer.
To use the CFC dynamic proxy, you will first need to create a reference to the class
com.compoundtheory.coldfusion.cfc.CFCDynamicProxy, as the methods we want to use to create the dynamic proxy are all static
.
For example:
CFCDynamicProxy =
javaloaderloader.create("com.compoundtheory.coldfusion.cfc.CFCDynamicProxy");
To create an actual dynamic proxy, the static method
createInstance is available on the CFCDynamicProxy object.
The two easiest ways to create a Dynamic Proxy from ColdFusion are is through one of the following method:
DynamicProxy.createInstance(cfc, interfaces)
or
DynamicProxy.createInstance(pathToCFC, interfaces)
parameter:
cfc The actual CFC to use in the dynamic proxy
parameter:
pathToCFC The absolute path to the CFC that you wish to create
parameter:
interfaces A string array of the Java interfaces that this CFC implements with its methods.
Please see the JavaDocs for other arguments that are available for
createInstance() For example, if I had a CFC name 'MyRunner' that has implemented the 'run' method of the Java Interface of
java.lang.Runnable, I would end up creating it as:
myRunner = createObject("component", "MyRunner").init();
runnerProxy = CFCDynamicProxy.createInstance(myRunner, ["java.lang.Runnable"]);
I could then pass my
runnerProxy to a
java.lang.Thread on construction, as to Java it doesn't know the
runnerProxy is anything but an instance of java.lang.Runnable, like so:
thread = createObject("java", "java.lang.Thread").init(runnerProxy);
thread.run();
ColdFusion Spring Integration
ColdFusion
Spring integration is
Spring Custom Schema, that leverages Spring's
dynamic scripting support and heavily, and uses many of its same configuration elements.
Note: This has been tested with Spring 2.5
Requirements
- ColdFusion 8 or 9
- Java 1.5+
- Including the following .jar's with the JavaLoader loadpaths:
- /javaloader/support/cfcdynamicproxy/lib/cfcdynamicproxy.jar
- /javaloader/support/spring/lib/spring-coldfusion.jar
- spring.jar (from Spring download)
- cglib-nodep-2.1_3.jar (from Spring download)
- loadColdFusionClassPath must be true
Java Build Path Requirements
If
you are using the Spring Integration within a Java Project, the
following libraries will need to be added to your Build Path so that it
can compile:
- /<coldfusion-home>/lib/cfusion.jar
- /<coldfusion-home>/runtime/lib/jrun.jar
API Documentation
JavaDoc DocumentationReference
This custom Spring schema enables you to create ColdFusion Components
in Spring that are wrapped in the ColdFusion Component Dynamic Proxy
that is also shipped with JavaLoader. Therefore, when Spring
encounters these Objects it treats them as if they were native Java
objects, implementing a given set of Java Interfaces.
Initialising Spring
Starting up Spring with JavaLoader requires some bootstrapping as we are using a custom ClassLoader, for example:
spring = javaloader.create("org.springframework.context.support.FileSystemXmlApplicationContext").init();
//we tell Spring to use JavaLoader's URLClassLoader for getting any Java Classes
spring.setClassLoader(javaloader.getURLClassLoader());
//tell Spring where our XML file is
spring.setConfigLocation("file://" & expandPath("./spring.xml"));
//lets load up our Spring.xml file.
spring.refresh();
This loads up Spring for us within JavaLoader.
Configuring the JavaLoader Spring-ColdFusion Schema
To add the JavaLoader Spring-ColdFusion schema to your Spring.xml file, add the following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:coldfusion="http://www.compoundtheory.com/javaloader/schema/coldfusion"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.compoundtheory.com/javaloader/schema/coldfusion
http://www.compoundtheory.com/javaloader/schema/spring-coldfusion.xsd"
>
...
</beans>
This will give you access to the <coldfusion:> namespace in your XML configuration file.
Defining a ColdFusion Component in Spring
To define a ColdFusion Component in Spring:
<coldfusion:cfc id="message"
script-source="file:///model/Message.cfc"
script-source-relative="true"
script-interfaces="com.IMessage"
/>
attribute:
id The
id of the defined Bean.
attribute:
script-source
The path to the CFC that is to be instantiated by this been definition.
attribute:
script-source-relative An optional attribute, that defines if the
script-source attribute is relative to the page context (true), or an absolute path (false). By default, this is set to 'true'.
attribute:
script-interfaces
A comma delimited list of Java interfaces that the dynamic proxy will
implement, and the methods the ColdFusion component will also implement.
There are several more optional attributes and child elements available that follow Spring
<bean> definition guidelines.
Downsides to using Spring to define your ColdFusion Components
There are two downsides to defining your ColdFusion Components within Spring that you should be aware of:
- There
is a slight performance penalty for invoking ColdFusion Components
through a dynamic proxy. Since most of the CFCs defined in Spring
will be singletons this performance hit should be negligible.
- You
can no longer user onMissingMethod with ColdFusion Components, as they
are defining a Java Interface, and only those methods can be called by
either other Java Objects or ColdFusion code.
Memory Consumption
Due to the way classes are cached in ColdFusion, URLClassLoaders will
not to be garbage collected. It is advised that instances of JavaLoader
are stored in the
Server scope, so that they never time out, and thus avoid this memory leak.
It should also be noted, that during development time (i.e. when
recreating JavaLoader for new changes to take effect), there will be a
leak into the PermGen memory space, so it may be required to restart
ColdFusion occasionally to clear it. This will not be something
that will effect a production application, as if JavaLoader is stored in
the Server scope, it will never be removed.
An example of this, with proper locking would be:
//this is my unique key for my JavaLoader instance in the server scope
//please don't use this key for your own applications.
uuid = "22713350-0490-11df-8a39-0800200c9a66";
paths = getPathsToMyJars();
<cfif NOT StructKeyExists(server, uuid)>
<cflock name="#application.applicationname#.server.JavaLoader" throwontimeout="true" timeout="60">
<cfscript>
if(NOT StructKeyExists(server, uuid))
{
server[uuid] = createObject("component",
"javaloader.JavaLoader").init(paths);
}
</cfscript>
</cflock>
</cfif>
This
could also be done in onApplicationStart() as well, which would
mitigate the need for locking, depending on how you architect your
applications.
This could also be further encapsulated within a ColdFusion component method structure.
Integration
Previously JavaLoader was a simple CFC that was very portable, and
while JavaLoader now has dependencies, it can still be integrated into
existing applications quite easily.
The only aspect that
much be maintained is the folder and file contents must sit in the same
directory structure as it currently is. Other than that,
JavaLoader can be integrated into existing applications and frameworks
quite easily. It is not required that JavaLoader be placed in the
root directory of the web directory or have a mapping for it to actually
work.
Shared Hosting
It should be noted that various ColdFusion administrator settings that
may be turned on in shared hosting, such as turning off access to Java
Objects, or disabling access to ColdFusion internal components can cause
issues with JavaLoader and/or make certain aspects of it not work
entirely.
If you are deploying to a shared hosting server, it is
highly recommended you test any of the JavaLoader features on that
server before commencing development to ensure there are no issues.