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:
  1. JavaLoader 1.0
    1. Examples
    2. Class Loading
      1. Requirements
      2. API Documentation
      3. Reference
    3. Dynamic Compilation
      1. Requirements
      2. API Documentation
      3. Reference
    4. ColdFusion Component Dynamic Proxy
      1. Requirements
      2. Java Build Path Requirements
      3. API Documentation
      4. Reference
    5. ColdFusion Spring Integration
      1. Requirements
      2. Java Build Path Requirements
      3. API Documentation
      4. Reference
        1. Initialising Spring
        2. Configuring the JavaLoader Spring-ColdFusion Schema
        3. Defining a ColdFusion Component in Spring
        4. Downsides to using Spring to define your ColdFusion Components
    6. Memory Consumption
    7. Integration
    8. Shared Hosting


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

API Documentation

ColdDoc Documentation
NetworkClassLoader JavaDoc

Reference

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

API Documentation

ColdDoc Documentation

Reference

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

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:

If you need access to any of the J2EE classes, you can import them from:

API Documentation

JavaDoc Documentaton

Reference

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


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:

API Documentation

JavaDoc Documentation

Reference

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:
  1. 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.
  2. 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.