Welcome!

ColdFusion Authors: Elizabeth White, Joseph Galarneau, Maureen O'Gara, Todd Anglin, Fuat Kircaali

Related Topics: ColdFusion

ColdFusion: Article

Reading File as from Java

Sometimes using cffile isn't enough - here's how Java can help

I open my e-mail this morning to find that one of my clients had sent me a file for import to their database. The file is comma delimited. Importing it into SQL Server is fairly simple with Data Transformation Services (DTS), however there's a catch.

The file contains a list of employees and EmployeeIDs. The EmployeeIDs are considered super sensitive, and must be encrypted. DTS can't be used to encrypt data before storing it in the database, so I'll need to do something else. The client didn't send me encrypted data, so what do I do?

Dissecting the Problem
In the past we've used a custom tag to encrypt the data with a BlowFish encryption scheme. (Note: This encryption is built into CFMX7, but the client hasn't upgraded yet). I can perform the encryption in ColdFusion. Maybe I can use cffile to read in the information? Let's see, the file has 60,000 records. The thought of loading so much data into memory and then having to parse it via CF does not sound like a fun project. Is there another way? Well, yes there is.

For 99.9% of the tasks that you perform daily, you'll never need to look beyond the functionality that ColdFusion provides to you, right out of the box. However, once in a while you come across something that you just can't do easily (or at all) from within ColdFusion. Thankfully, ColdFusion is now built on top of Java and you have access to the full array of Java-based objects. Random file access can be done in Java, but not from within CF. Random file is just a fancy way to say that you can access any point in the file, usually by providing the character number of the file. Using some specialized methods, we can also read a file line by line, which is the example we'll explore more in this article.

Here is a simplified version of our client file:
Last Name, First Name, EmployeeID
Arehart, Charlie, 78934
Helms, Hal, 78903
Horwith, Simon, 54321
Houser, Jeff, 12345

The first record in the file is a header record, naming the columns and the order they appear. Following the header row we have lots of data. For simplicity, I've cut out a lot of the information you would probably receive from the client, such as contact information and company position. The goal is to read in one line from the file, process the record, and then repeat until there are no more entries in the file.

There are two Java classes that we need to make use of to be able to read in the file. The first is BufferedReader class, or java.io.BufferedReader. This class is used to read text from any character input stream. Various items can be used as input streams, but in our case we want to use a text file. Full documentation for the BufferedReader class is located here: http://java.sun.com/j2se/1.4.2/docs/api/java/io/BufferedReader.html.

A constructor, or init method, is a way to initialize a Java object, and many Java objects have one or more constructors. There are two constructors of the BufferedReader class. Both accept a Reader object. One also accepts the size of the input buffer stream. For our purposes we can use the default input buffer stream. But, what is a reader object, and where do we get it?

A reader object is an object created from the reader class. Due to the nature of Java, any subclasses of the reader class can also be accepted as constructor argument. Documentation on the reader class is located here http://java.sun.com/j2se/1.4.2/docs/api/java/io/Reader.html, but the subclass of reader that we are really interested in is the FileReader class, located here: http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileReader.html. The FileReader class is designed for reading in information from a file. It has three different constructors, but the one we are going to use accepts a string that is the location of the file.

The process to follow is to create and init the FileReader class. Then create and init the BufferedReader class using the FileReader class. After that, you can call various methods on the BufferedReader class, such as read to get a specified number of characters or readLine to read a full line of text. All this is interesting, but how do we access the Java Classes from within ColdFusion?

Creating the Java Objects with cfobject and CreatObject
There are two ways you can create Java objects with ColdFusion. One method uses a tag, and the other users a function. They are both similar, so I'll teach you both of them. From within CFML, you can use the cfobject tag; In CFScript the CreateObject tag is your tool for object creation. The attributes for the tag are very similar to the function's parameters. This is the list of parameters:

  • Type: cfobject can be used to create many types of different objects, such as Web services, CFCs, and Java objects. The type attribute is used to specify what kind of object you are creating. Since we are creating a Java object, we'll give it the value of "Java". This is also the first parameter passed into the CreateObject function. It is required in both methods.
  • Class: The class attribute is used to define the name of the Java class that you want to create. There are many Java objects for various different purposes. You can find the complete list here http://java.sun.com/j2se/1.4.2/docs/api/allclasses-noframe.html. The class is the second and final parameter of the CreateObject function. It is required.
  • Action: The action attribute is used to tell the tag to create the object. Its only valid value is "create". It is required for the cfobject tag, but has no parallel in the CreateObject function.
  • Name: The name attribute specifies the name of the variable that will contain the reference to the newly created object. It is required for the cfobject tag. When using the CreateObject function, this value is the one on the left side of the equal sign, similar to a cfset.
To use cfobject create an object of the FileReader class you would use code like this:

<cfobject type="Java" class=" java.io.FileReader" Action="Create" name="fileReader">

To implement the same command using the CreateObject Function, you might use code like this:


<cfset FileReader = CreateObject("Java"," java.io.FileReader")>

Or you could execute the function inside of a CFScript block, like this:


<cfscript>
 FileReader = CreateObject("Java"," java.io.FileReader");
</cfscript>

All Java objects have a collection of methods associated with them. The names and functionalities of these methods depend upon which object you are creating an instance of. To call a method against an object you must use something called Object Property notation. You are probably already familiar with Object Property notation, because you can use it to reference columns against a query variable, or keys in a structure, or methods in a CFC.

Object Property notation consists of the variable name of the object, followed by a dot, then the property or method you want to access. If calling a method, enclose the argument list in parenthesis. Most Java objects have one or more init methods. One of the init methods on our FileReader object accepts the disk location of the file. You'll probably call it like this:


<cfset Result = fileReader.init("C:\test.txt");

Since Java does not have named arguments, you cannot use cfinvoke to call methods against a Java object. Many developers prefer to use CFScript when invoking and using objects because the Object Property notation lends itself better to CFScript than to CFML.

Reading the File
Let's take a look at the code:


<cfscript>
 fileReader = CreateObject("java", "java.io.FileReader");
 fileReader.init("C:\\test.txt");
 br = CreateObject("java", "java.io.BufferedReader");
 br.init(fileReader);
 
 // Counters for the loop
 TotalNumRecords = 0;
</cfscript>

<cftry>
 <cfloop condition="true">
 <cfscript>
  line = br.readLine();
 </cfscript>
 <Cfset TotalNumRecords = TotalNumRecords + 1>
 <cfoutput>
  Line #TotalNumRecords#: #Line#<br>
  <!---- other processing ---->
 </cfoutput>
 </cfloop>
 <cfcatch type="coldfusion.runtime.
	UndefinedVariableException">
 <!--- this indicates end of file, ok to ignore error --->
 </cfcatch>

</cftry>

As discussed earlier, the code starts out by creating a FileReader object. The full path name for the FileReader is java.io.FileReader. Then it initializes that object, giving it the location of a file. The next line creates the BufferedReader object. The full path name for the Buffered-Reader object is java.io.BufferedReader. The init method on BufferedReader object uses the fileReader object as input.

The code does not assign the results of the init operation to any variable, although you could if you wanted. ColdFusion returns the name of the Java object and its location in memory. There isn't much you can do with the information in the context of ColdFusion.

Normally, in Java or other programming languages, you would loop over the input stream until you received an end of file character. Within each loop you would read the next line. CF doesn't have the concept of an end of file character, but we can work around this using the cftry and cfcatch error tags. The code uses a condition loop, with the condition being true. That means the loop will never end. However, the loop is inside a cftry block and the cfcatch is outside of the loop. The catch block will catch an undefined variable exception. When the code in the loop reads the next line and it's not there, this exception will catch when it tries to read the variable.

The code inside the loop is the simple part. It uses the readLine method on the BufferedReader variable to get the next line. It increments counter variable TotalNumRecords and displays the line to the screen. Any additional processing, such as encrypting the data and storing it in the database, is left out of this example, because you know how to do all that.

If not, read up on encryption here http://livedocs.macromedia.com/coldfusion/ 6.1/htmldocs/functi75.htm#wp1104201 and using the cfquery tag http://livedocs.macromedia.com/coldfusion/ 6.1/htmldocs/tags-b19.htm#wp1102316.

A Final Thought about CF, Macromedia, and Adobe
I am writing this article fewer than 24 hours after Adobe and Macromedia announced that they would be merging into a single company yet. The mailing lists and blogs are awash with a mix of despair and excitement, or sometimes both. When I write articles like this, I can't help but think about the power that lies in our hands with the ability to write ColdFusion code. No matter what the future plans are, I can't help but look forward with optimism. Rest assured I hope that this event will officially end the "Microsoft wants to buy Macromedia" doomsday threads.

More Stories By Jeffry Houser

Jeffry is a technical entrepreneur with over 10 years of making the web work for you. Lately Jeffry has been cooped up in his cave building the first in a line of easy to use interface components for Flex Developers at www.flextras.com . He has a Computer Science degree from the days before business met the Internet and owns DotComIt, an Adobe Solutions Partner specializing in Rich Internet Applications. Jeffry is an Adobe Community Expert and produces The Flex Show, a podcast that includes expert interviews and screencast tutorials. Jeffry is also co-manager of the Hartford CT Adobe User Group, author of three ColdFusion books and over 30 articles, and has spoken at various events all over the US. In his spare time he is a musician, old school adventure game aficionado, and recording engineer. He also owns a Wii. You can read his blog at www.jeffryhouser.com, check out his podcast at www.theflexshow.com or check out his company at www.dot-com-it.com.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.