The cfobject
tag can call any Java class that is available on the class path specified on the ColdFusion Administrator JVM and Java Settings page. For example:
<cfobject
type="Java" class="MyClass" name="myObj">
Although this tag loads the class, it does not create an instance object. Static methods and fields are accessible after the call to cfobject
.
To call the constructors explicitly, use the init
method with the appropriate arguments; for example:
<cfset ret=myObj.init(arg1, arg2)>
If you call a public method on the object without first calling the init
method, the result is an implicit call to the default constructor.
Arguments and return values can be any valid Java type; for example, simple arrays and objects. ColdFusion does the appropriate conversions when strings are passed as arguments, but not when they are received as return values.
The following sections provide more details on calling Java objects in ColdFusion.
Java is a strongly typed language, unlike ColdFusion, which does not enforce data types. As a result, there are some subtle considerations when calling Java methods. The following sections create and use a Java class to illustrate how to use Java effectively in ColdFusion pages.
The Employee class has four data members: FirstName and LastName are public, and Salary and JobGrade are private. The Employee class has three overloaded constructors and a overloaded SetJobGrade method.
Save the following Java source code in the file Employee.java, compile it, and place the resulting Employee.class file in a directory that is specified in the class path.
public class Employee {
public String FirstName; public String LastName; private float Salary; private int JobGrade; public Employee() { FirstName =""; LastName =""; Salary = 0.0f; JobGrade = 0; } public Employee(String First, String Last) { FirstName = First; LastName = Last; Salary = 0.0f; JobGrade = 0; } public Employee(String First, String Last, float salary, int grade) { FirstName = First; LastName = Last; Salary = salary; JobGrade = grade; } public void SetSalary(float Dollars) { Salary = Dollars; } public float GetSalary() { return Salary; } public void SetJobGrade(int grade) { JobGrade = grade; } public void SetJobGrade(String Grade) { if (Grade.equals("CEO")) { JobGrade = 3; } if (Grade.equals("MANAGER")) { JobGrade = 2; } if (Grade.equals("DEVELOPER")) { JobGrade = 1; } } public int GetJobGrade() { return JobGrade; } }
Save the following text as JEmployee.cfm:
<html>
<body> <cfobject action=create type=java class=Employee name=emp> <!-- <cfset void = emp.init()> --> <cfset emp.firstname="john"> <cfset emp.lastname="doe"> <cfset firstname=emp.firstname> <cfset lastname=emp.lastname> </body> <cfoutput> Employee name is #firstname# #lastname# </cfoutput> </html>
When you view the page in your browser, you should get the following output:
The following table describes the CFML code and its function:
Keep the following points in mind when you write a ColdFusion page that uses a Java class object:
The following CFML page explicitly calls one of the alternate constructors for the Employee object:
<html>
<body> <cfobject action=create type=java class=Employee name=emp> <cfset emp.init("John", "Doe",100000.00, 10 )> <cfset firstname=emp.firstname> <cfset lastname=emp.lastname> <cfset salary=emp.GetSalary()> <cfset grade=emp.GetJobGrade()> </body> <cfoutput> Employee name is #firstname# #lastname# Employee salary and Job Grade #Salary# #grade# </cfoutput> </html>
In this example, the constructor takes four arguments; the first two are strings, and third is a float, and the fourth is an integer.
Cold Fusion is a typeless scripting language (that is, it does not use explicit data types) while Java is strongly typed.
Under most situations, when the method names are not ambiguous, ColdFusion can determine the required types. For example, ColdFusion strings are implicitly converted to the Java String type. Similarly, if a Java object contains a doIt method that expects a parameter of type int, and CFML is issuing a doIt call with a CFML variable x, ColdFusion will converts the variable x to Java int type. However, ambiguous situations can result from Java method overloading, where a class has multiple implementations of the same method that differ only in their parameter types.
The following sections describe how ColdFusion handles the unambiguous situations, and how it provides you with the tools to handle ambiguous ones.
Whenever possible, ColdFusion matches Java types to ColdFusion types as listed in the following table. ColdFusion does not support direct conversion of Date/Time variables and structures.
You can overload Java methods so a class can have several identically named methods. At runtime, the VM resolves the specific method to use based on the parameters passed in the call and their types.
In the section "Example: the Employee class", the Employee class has two implementations for the SetJobGrade method. One method takes a string variable, the other an integer. If you write code such as the following, which implementation to use is ambiguous:
<cfset emp.SetJobGrade("1")>
The "1" could be interpreted as string or as number, so there is no way to know which method implementation to use. When a ColdFusion encounters such an ambiguity, it throws a user exception.
The ColdFusion JavaCast
function helps you resolve such issues by specifying the Java type of a variable, as in the following line:
<cfset emp.SetJobGrade(JavaCast("int", "1")>
The JavaCast
function takes two parameters: a string representing the Java data and the variable whose type your are setting. You can specify the following Java data types: bool, int, long, float, double, and String.
For more information on the JavaCast
function, see CFML Reference.
Use the cftry
and cfcatch
tags to can catch exceptions thrown by Java objects. Use the CFML GetException
function to retrieve the Java exception object. The following example demonstrates the GetException
function.
The following Java code defines the foo class that throws a sample exception. It also defines a fooException class that extends the Java built-in Exception class and includes a method for getting an error message.
public class foo {
public foo() { } public void doException() throws fooException { throw new fooException("I am throwing a throw Exception "); } } Class fooException public class fooException extends Exception { public String GetErrorMessage() { return "Error Message from fooException"; } }
The following CFML code calls the foo class doException method. The cfcatch
block handles the resulting exception by calling the CFML GetException
function to retrieve the Java exception object and then calling the object's GetErrorMessage method to get the error information.
<cfobject action=create type=java class=foo name=Obj>
<cftry> <cfset VOID = Obj.doException() > <cfcatch type="Any"> <cfset exception=GetException(Obj)> <cfset message=exception.GetErrorMessage()> <cfoutput> <br>The exception message is: #message#<br> </cfoutput> </cfcatch> </cftry>
The following table describes the code and its function:
Note that after you call GetException, the exception object is just like any other Java component object, and you can call any methods on it.
In ColdFusion prior to version 5, Java classes were loaded on demand and they were not unloaded until the server restarted. This is the way a typical Java application works and is appropriate for production systems. However, if you change a Java method implementation, you must shut down the server and restart it before ColdFusion can use the new class implementation.
In version 5, ColdFusion Server uses a custom class loader to load Java classes on the fly, similar to Java Servlet engines. This enables you to modify Java method implementations and use the new code from ColdFusion without restarting the server.
ColdFusion 5 introduces the concept of dynamic load path, a directory that you can specify on Cold Fusion Administrator JVM and Java Settings page. You deposit your Java class files in this directory when you update the class implementation. ColdFusion checks the class file time stamp when an object of the class is created. If ColdFusion detects a new class file, it loads the class from that directory.
To use this feature, make sure that the Java implementation classes that you modify are not in the general JVM class path. In addition, do not package the classes into jar files or zip files. In all other ways, ColdFusion Class loader follows Java conventions including those for package names and directory name mapping.
Suppose the directory "C:\classes" is the designated "hot" dynamic class path and you have a class com.Allaire.Employee. You put the Employee.class file in the following location:
C:\ classes\com\Allaire\Employee.class
The dynamic class-loading feature is meant for development uses, and incurs a slight performance penalty associated with checking time stamps during disk IO operations. For that reason, you do not use this feature in a production environment where there might be high volume of traffic.
To disable automatic class loading, put all classes in the normal Java class path. Classes located on the Java class path are loaded once per server lifetime and can only be reloaded by stopping and restarting ColdFusion Server.
The following code provides a more complete example of using Java with cfobject.
The Example class manipulates integer, float, array, Boolean, and Example object types.
The following Java code defines the Example class. The Java class Example has one public integer member, mPublicInt
. Its constructor initializes mPublicInt
to 0 or an integer argument. The class has the following public methods:
public class Example {
public int mPublicInt; public Example() { mPublicInt = 0; } public Example(int IntVal) { mPublicInt = IntVal; } public String ReverseString(String s) { StringBuffer buffer = new StringBuffer(s); return new String(buffer.reverse()); } public String[] ReverseStringArray(String [] arr) { String[] ret = new String[arr.length]; for (int i=0; i < arr.length; i++) { ret[arr.length-i-1]=arr[i]; } return ret; } public int Add(int a, int b) { return (a+b); } public float Add(float a, float b) { return (a+b); } public Example Add(Example a, Example b) { return new Example(a.mPublicInt + b.mPublicInt); } static public int SumArray(int[] arr) { int sum=0; for (int i=0; i < arr.length; i++) { sum += arr[i]; } return sum; } static public Example SumObjArray(Example[] arr) { Example sum= new Example(); for (int i=0; i < arr.length; i++) { sum.mPublicInt += arr[i].mPublicInt; } return sum; } static public int[] ReverseArray(int[] arr) { int[] ret = new int[arr.length]; for (int i=0; i < arr.length; i++) { ret[arr.length-i-1]=arr[i]; } return ret; } static public boolean Flip(boolean val) { System.out.println("calling flipboolean"); return val?false:true; } }
The following useExample.cfm page uses the Example
class to manipulate numbers, strings, Booleans, and Example objects. Note the use of the JavaCast
CFML function to ensure that CFML variables convert into the appropriate Java data types.
<html>
<head> <title>CFOBJECT and Java Example</title> </head> <body> <!--- Create a reference to an Example object ---> <cfobject action=create type=java class=Example name=obj> <!--- Create the object and initialize its public member to 5 ---> <cfset x=obj.init(JavaCast("int",5))> <!--- Create an array and populate it with string values, then use the Java object to reverse them. ---> <cfset myarray=ArrayNew(1)> <cfset myarray[1]="First"> <cfset myarray[2]="Second"> <cfset myarray[3]="Third"> <cfset ra=obj.ReverseStringArray(myarray)> <!--- Display the results ---> <cfoutput> <br> original array element 1: #myarray[1]#<br> original array element 2: #myarray[2]#<br> original array element 3: #myarray[3]#<br> after reverse element 1: #ra[1]#<br> after reverse element 2: #ra[2]#<br> after reverse element 3: #ra[3]#<br> <br> </cfoutput> <!--- Use the Java object to flip a Boolean value, reverse a string, add two integers, and add two float numbers ---> <cfset c=obj.Flip(true)> <cfset StringVal=obj.ReverseString("This is a test")> <cfset IntVal=obj.Add(JavaCast("int",20),JavaCast("int",30))> <cfset FloatVal=obj.Add(JavaCast("float",2.56),JavaCast("float",3.51))> <!--- Display the results ---> <cfoutput> <br> StringVal: #StringVal#<br> IntVal: #IntVal#<br> FloatVal: #FloatVal#<br> <br> </cfoutput> <!--- Create a two-element array, sum its values, and reverse its elements ---> <cfset intarray=ArrayNew(1)> <cfset intarray[1]=1> <cfset intarray[2]=2> <cfset IntVal=obj.sumarray(intarray)> <cfset reversedarray=obj.ReverseArray(intarray)> <!--- Display the results ---> <cfoutput> <br> IntVal1 :#IntVal#<br> array1: #reversedarray[1]#<br> array2: #reversedarray[2]#<br> <br> </cfoutput><br> <!--- Create a ColdFusion array containing two Example objects. Use the SumObjArray method to add the objects in the array Get the public member of the resulting object---> <cfset oa=ArrayNew(1)> <cfobject action=create type=java class=Example name=obj1> <cfset VOID=obj1.init(JavaCast("int",5))> <cfobject action=create type=java class=Example name=obj2> <cfset VOID=obj2.init(JavaCast("int",10))> <cfset oa[1] = obj1> <cfset oa[2] = obj2> <cfset result = obj.SumObjArray(oa)> <cfset intval = result.mPublicInt> <!--- Display the results ---> <cfoutput> <br> intval1: #intval#<br> <br> </cfoutput><br> </body> </html>