Saturday, November 28, 2009

OT: This is Unicode ?

While visiting the site, I came across this unexpected title from 1992. (Yes, it really is about unicode. Who says unicode is a dry topic ;).

Kiss Your ASCII Goodbye (John Dvorak, PC Magazine/1992)

...Read More

Friday, November 27, 2009

SOT: Heads or Tails? (Windows Resource Kit - Tail.exe Gotcha)

So I was comparing two windows implementations of tail, and came across something unexpected. While I was checking one of my tests, I noticed the results contained less lines than I requested. The base files definitely contained more than enough lines. So I knew that was not problem. After digging deeper, I discovered the lines returned were not even from the end of the file! They were from the middle.

Well, come to find out the file I was testing contained some funky characters. One of them being ascii character 26. Apparently the Windows Resource Kit version of tail.exe interprets ascii 26 as some sort of EOF (end of file) marker. Consequently tail.exe uses that marker as its starting point. Well, naturally that skews the results because that position is not necessarily the actual end of the file.

To see it for yourself, create a simple file containing 50 lines, with the ascii character 26 inserted every ten (10) lines.

<cfset specialChar  = chr(26)  />
<cfset newLine = chr(10) />
<cfsavecontent variable="data">
<cfloop from="1" to="50" index="x">Log file line #x# <cfif not x mod 10>#specialChar#</cfif>#newLine#</cfloop>

<cfset filePath = ExpandPath("./testLogFile.log") />
<cfset FileWrite( filePath, trim(data) ) />

Now use tail.exe to grab the last twenty (20) lines of the file. Due to the placement of the special characters, what you will end up with is the first 10 lines of the file, not the last twenty.

<cfset grabLines = 20 />
<cfexecute name='"c:\program files\Windows Resource Kits\tools\tail.exe"'
arguments='-#grabLines# #filePath#'
timeout='20' />

<!--- convert lines to array --->
<cfset linesFound = listToArray(output, chr(10), true) />
<cfset actualLines = listToArray( FileRead( filePath ), chr(10), true) />

<p><strong>Log File:</strong> #filePath# </p>
<p><strong>Requested Lines:</strong> #grabLines# </p>
<p><strong>Lines Found:</strong> #arrayLen(linesFound)#</p>
<p><strong>Actual Lines:</strong> #arrayLen(actualLines)#</p>

Fortunately, the GNUWin32 version does not suffer from this "problem" and returns the expected results. (Thank goodness someone knows basic subtraction)

As for the Windows Resource Kit version, well.. if we were flipping a coin, this time it was definitely "heads" - as in "head on over to and get a yourself a working copy of tail.exe".

...Read More

Wednesday, November 25, 2009

OT: JAI Resources?

Does anyone know of any good resources (online, books, etcetera) on JAI? Frankly the sun docs leave a lot to be desired. If anyone can recommend a good book on the topic it would be greatly appreciated.

...Read More

Tuesday, November 24, 2009

ColdFusion 8: Blowfish Encryption (Something smells fishy .. and it is not the algorithm)

A while back I saw a thread on the adobe forums about an issue with Blowfish encryption. The issue being the results from ColdFusion's encrypt function seemed to differ from other languages. Hardly uncommon given the "delicate" nature of encryption. What grabbed my attention was that the results from ColdFusion and Java were also different. Not what I would have expected.

Take the simple example below. It sure seems like the same text and key are used for both CF and Java encryption. Yet the results are different. However, if you compare the java results with those from the online tools (mentioned in the adobe thread) they are all the same. The odd one out here is ColdFusion.

Note: These examples were chosen for simplicity. They are NOT a good example of strong encryption (usage of ECB, etcetera ...)


<cfset myText        = "guess this!1 2 3" />
<cfset myKey         = generateSecretKey("Blowfish", 32) />
<cfset cfEncrypted   = Encrypt(myText, myKey, "Blowfish/ECB/NoPadding", "Hex") />

<!--- results (using 

tags because the blogger eats line breaks!) ---> <strong>ColdFusion:</strong><hr /> <cfoutput> <p><strong>MyText</strong> : #myText#</p> <p><strong>MyKey</strong> : #myKey#</p> <p><strong>CF Encrypted</strong> : #cfEncrypted#</p> </cfoutput>

<!---  generate a secret key --->
<cfset SecretKeySpec     = createObject("java", "javax.crypto.spec.SecretKeySpec") />
<cfset myKeyBytes         = CharsetDecode(myKey, "utf8") />
<cfset keySpec             = SecretKeySpec.init(myKeyBytes, "Blowfish") />
<!--- get a cipher instance for encrypting --->
<cfset Cipher             = createObject("java", "javax.crypto.Cipher") />
<cfset encryptor         = Cipher.getInstance("Blowfish/ECB/NoPadding") />
<cfset encryptor.init(Cipher.ENCRYPT_MODE, keySpec) />
<!--- do the encryption --->
<cfset myTextBytes         = CharsetDecode(myText, "utf8") />
<cfset javaEncrypted     = encryptor.doFinal(myTextBytes) />

<!--- results --->
<p><strong>Java Encrypted</strong>  : #BinaryEncode(javaEncrypted, "hex")# </p>

Sample Results:

Since I am definitely not an expert on encryption, I just figured I was missing something. Eventually I did get the CF and java results to match by converting the secret key to binary and then back to base64.

<cfset myText        = "guess this!1 2 3" />
<cfset myKey         = generateSecretKey("Blowfish", 32) />
<cfset magicKey      = BinaryEncode(CharsetDecode(myKey, "utf8"), "base64") />
<cfset cfEncrypted   = Encrypt(myText, magicKey, "Blowfish/ECB/NoPadding", "Hex") />

The new results clearly showed my magicKey now matched the java keySpec value. So of course the encrypted strings also matched.

But I am not sure why that the extra step necessary. Supposedly the value returned from GenerateSecretKey() is already base64 encoded. Also, it seems to work correctly in reverse (ie when the secret key is generated from the javax.crypto.KeyGenerator).

So am I missing something here, or is this a "feature"?

Update: Yep! I was obviously staring at this for too long. I missed something obvious. I should have used BinaryDecode(myKey, "base64") to get the java key bytes and vice versa. Now it make sense! ;)

As mentioned in the comments, using CBC mode is a bit different. As it requires an IV (Initialization Vector). Here is a basic example using CBC mode. Though in practice, you probably would not use the same IV each time as I am doing here. I also included a lot of extra debugging output. So you can verify the values used by both ColdFusion and Java are the same.

<cfset algorithm      = "Blowfish/CBC/PKCS5Padding">
<cfset myText        = "can you guess this?" />
<cfset myKey         = generateSecretKey("Blowfish", 32) >
<cfset myIV             = CharsetDecode("12345678", "utf8")>
<cfset cfEncrypted   = Encrypt(myText, myKey, algorithm, "base64", myIV) />

<!--- results (using <p> tags because the blogger eats line breaks!) --->
<strong>ColdFusion (Encrypted):</strong><hr />
    <p><strong>MyText</strong> : #myText#</p> 
    <p><strong>MyKey</strong> : #myKey#</p>  
    <p><strong>MyIV</strong> : <cfdump var="#myIV#"></p>  
    <p><strong>CF Encrypted</strong> : #cfEncrypted#</p> 

<cfset SecretKeySpec       = createObject("java", "javax.crypto.spec.SecretKeySpec") />
<cfset myKeyBytes          = BinaryDecode(myKey, "base64") />
<cfset keySpec             = SecretKeySpec.init(myKeyBytes, "Blowfish") />
<cfset ivSpec               = createObject("java", "javax.crypto.spec.IvParameterSpec").init( myIV )>
<cfset Cipher              = createObject("java", "javax.crypto.Cipher") />
<cfset encryptor           = Cipher.getInstance( algorithm ) />
<cfset encryptor.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec) />
<cfset myTextBytes         = CharsetDecode(myText, "utf8") />
<!--- convert encrypted byte array to base64 string --->
<cfset javaEncrypted       = BinaryEncode( encryptor.doFinal(myTextBytes), "base64") />

<!--- results (using <p> tags because the blogger eats line breaks!) --->
<strong>Java Encrypted:</strong><hr />
    <p><strong>myTextBytes (utf8)</strong> : #CharsetEncode(myTextBytes, "utf8")#</p>  
    <p><strong>keySpec</strong> : #BinaryEncode(keySpec.getEncoded(), "base64")#</p> 
    <p><strong>ivSpec (raw)</strong> : <cfdump var="#ivSpec.getIV()#"></p>  
    <p><strong>ivSpec (utf8)</strong> : #CharsetEncode(ivSpec.getIV(), "utf8")#</p>  
    <p><strong>Java Encrypted</strong>  : #javaEncrypted# </p>

Now just to prove the results are compatible, you can decrypt the ColdFusion results with Java (and vice versa).

<cfset SecretKeySpec     = createObject("java", "javax.crypto.spec.SecretKeySpec") />
<cfset myKeyBytes        = BinaryDecode(myKey, "base64") />
<cfset keySpec           = SecretKeySpec.init(myKeyBytes, "Blowfish") />
<cfset ivSpec             = createObject("java", "javax.crypto.spec.IvParameterSpec").init( myIV )>
<cfset Cipher            = createObject("java", "javax.crypto.Cipher") />
<cfset decryptor         = Cipher.getInstance( algorithm ) />
<cfset decryptor.init(Cipher.DECRYPT_MODE, keySpec, ivSpec) />
<!--- convert our encrypted (base64 encoded) string to a byte array --->
<cfset encryptedBytes     = BinaryDecode( cfEncrypted , "base64") />
<!--- the decrypted bytes will be in UTF8 --->
<cfset javaDecrypted      = CharsetEncode( decryptor.doFinal( encryptedBytes ), "utf8") />

    <p><strong>Java Decrypted</strong>  : #javaDecrypted# </p>

<!--- decrypt (base64 encoded) encrypted string --->
<cfset cfDecrypted   = Decrypt(javaEncrypted, myKey, algorithm, "base64", myIV) />

    <p><strong>ColdFusion Decrypted</strong>  : #cfDecrypted# </p>

...Read More

Thursday, November 19, 2009

Seeing Stripes: Experiment with ZXing

I recently took a look at the ZXing Project which is described as "an open-source, multi-format 1D/2D barcode image processing library implemented in Java.". While its primary focus is mobile phones, I decided to look at the javase component of the project. ZXing also comes with a servlet. But I first wanted to see how the lower level components worked.

Not seeing any pre-compiled jars, I downloaded the latest sources ( After loading it into Eclipse, and poked around a bit to figure out how to decode barcode images of different types. Finally, I used the pre-made ant files to create the two jars I needed: core.jar and javase.jar.

Decoding barcode images
While the project has some decent documentation, the developer notes seem to be outdated in a few places. But examining some of the javase classes turned up a convenience class called MultiFormatReader. From what I could tell it provides a simple way to decode barcodes when you may not know the type ahead of time. Obviously if you know the barcode type ahead of time, it is more efficient to use the specific reader for that type. But since I wanted to test all supported types the MultiFormatReader was perfect for my needs.

Now before I could test ZXing, I needed some barcode images. So I used CFBarbecue.cfc to generate a sample of all of the barcodes supported by the Barbecue library. Now ZXing does not support all of them. But there is a good intersection between the two. Once I had my images, I created an instance of MultiFormatReader, using the ever-handy JavaLoader.cfc. I also passed the reader some hints to increase accuracy.

Note: This code is for demonstration purposes only. Normally, the javaLoader should be stored in the server scope to avoid memory leaks.

<cfset paths = [] />
<cfset arrayAppend(paths, expandPath('zxing-1.4/core/core.jar')) />
<cfset arrayAppend(paths, expandPath('zxing-1.4/javase/javase.jar')) />
<cfset loader = createObject("component", "javaloader.JavaLoader").init( paths ) />

<!--- Create hints for the reader. TRY_HARDER increases accuracy --->
<cfset DecodeHintType = loader.create("") />
<cfset hints = loader.create("java.util.Hashtable").init() />
<cfset hints.put(DecodeHintType.TRY_HARDER, javacast("boolean", true)) />

<!--- Since we will be reading multiple images, create one reader and reuse it  --->
<cfset Reader = loader.create("").init() />
<cfset Reader.setHints(hints) />

Next, I used cfdirectory to grab a list of all of my barcode images. Then looped through the images and attempted to decode each one. Now in order to use the reader's decode method, I needed to convert my image to a compatible format: BinaryBitmap. So I read in each image, extracted the underlying BufferedImage and passed it through a few of the ZXing classes designed to prepare images for decoding.

Finally, I called the reader's decode method to decode the image. If the image was successfully decoded it returns a Result object containing some useful properties like the raw text and barcode type.

One thing to be aware of is that error handling is unlikely to yield any useful information, beyond the fact that an error occurred. From the notes in the current source code, the way in which decoding errors are handled is undergoing a transformation, to increase performance. So for now do not expect much exception information beyond name, rank and serial number.

<!--- read all barcode images in my subdirectory --->
<cfdirectory action="list" directory="#ExpandPath('images')#"
            name="barcodeImages" />

    Try and decode each barcode with z-xing.
    Note: Not all codes are supported by z-xing!!!
<cfoutput query="barcodeImages">
    <cfset imagePath = Directory &"/"& name />

        <!--- display the original image --->
        <strong>Barcode Image</strong> #name# <br />
        <cfimage action="writeToBrowser" source="#imagePath#"><br />

        <!--- extract the BufferedImage of the current barcode --->
        <cfset img = ImageGetBufferedImage(ImageRead(imagePath)) />

        <!--- prepare the image for decoding --->
        <cfset source = loader.create("").init(img) />
        <cfset binarizer = loader.create("").init(source) />
        <cfset bitmap = loader.create("").init(binarizer) />

        <!--- This method reuses the existing settings for decoding multiple images --->
        <cfset decodedResult = Reader.decodeWithState(bitmap) />

        <!--- display the results --->
        <strong>Decoded Text</strong> #decodedResult.getText()# <br />
        <strong>Decoded Format</strong> #decodedResult.getBarcodeFormat()# <br />

            Not supported or unable to decode: #cfcatch.type# #cfcatch.message#
    <br /><br /><hr />

All of my sample images were decoded except:

  • 2of7
  • Codabar
  • Int2of5
  • Monarch
  • NW7
  • PDF417
  • PostNet
  • Std2of5
  • USD4
While I knew some of them are not supported, but I was surprised that the PD417 and Interleaved 2 of 5 samples failed. I am not yet sure why. For now I will assume it was an error on my part. I will have to investigate the individual readers further.

However, I was able to decode a DataMatrix barcode using the DataMatrixReader. (Created using the free servlet at Just be aware the current project documentation lists DataMatrix decoding as "alpha quality". So do not expect miracles.

<cfset imagePath = "c:\barCode_loremIpsum_idAutomation.gif" />

<cfset DecodeHintType = loader.create("") />
<cfset hints = loader.create("java.util.Hashtable").init() />
<cfset hints.put(DecodeHintType.PURE_BARCODE, javacast("boolean", true)) />

<!--- prepare the image for decoding --->
<cfset img = ImageGetBufferedImage(ImageNew(imagePath)) />
<cfset source = loader.create("").init(img) />
<cfset binarizer = loader.create("").init(source) />
<cfset bitmap = loader.create("").init(binarizer) />

<!--- attempt to decode the image --->
<cfset Reader = loader.create("") />
<cfset decodedResult = Reader.decode( bitmap, hints ) />

<!--- display the original image --->
<strong>Barcode Image</strong> #imagePath# <br />
<cfimage action="writeToBrowser" source="#imagePath#"><br />

<!--- display the results --->
<strong>Decoded Result</strong><br />
#decodedResult.getText()# <hr />

Other Classes
The j2se component also has a few classes you might find useful for quick tests. The first is GUIRunner. It is a very rudimentary GUI you can use to test a single bar code image. To use it, simply open a command prompt and enter the following. Just modify the "-cp" value (classpath) to point to the location of your jars.


C:\test\zxing> java -cp c:\test\zxing\core.jar;c:\test\zxing\javase.jar

The other class is CommandLineRunner. It can be used to decode a single image, a directory of images or a url. Just supply the locations of your jars and the file/directory/url you wish to decode. The "--try_harder" hint can be used to increase accuracy, and the "--dump_results" option to save the results to a file. Note: The results of each barcode are saved to individual files. So keep that in mind if you are decoding an entire directory.


C:\test\zxing> java -cp c:\test\zxing\core.jar;c:\test\zxing\javase.jar c:/test/zxing/barcodeimages --try_harder

Well, that is it for now. I will have to experiment with some lower quality images, to see how the results stack up in a real test. (Once I find a supported phone ...)

...Read More

Wednesday, November 18, 2009

SOT: Who is the fairest browser of them all?

I have long been a fan of Firefox. But somewhere in between versions 2 and 3.5 it started behaving like a black-hole of resources, sucking up more and more memory, growing increasing sluggish. Not to mention taking forever to start up. So fickle user that I am, I have been trying out other browsers while I give Firefox one last chance before giving it the boot.

Of course one of the browsers I am (re-)trying is Chrome. I did use it briefly when it first came out. But quickly gravitated back to my beloved Firefox, with all of its wonderful extensions and flexible configuration. But I will admit Chrome is extremely fast. No news there. What was a nice surprise was its mini-Task-Manager (Shft-Esc) and the "Stats for Geeks" breakdown.

Meanwhile, I did some file shuffling and installed a completely fresh copy of FireFox 3.5.5. Just to see if I could get a sense of whether the true culprit was FireFox itself, a specific setting or one of my plugins. I tried to start with as little functionality and minimal settings as possible. Not all of them are relevant. But in the interest of full disclosure, here is a run down of the changes I made to the default settings:

- *Disabled* all of the plugins detected by default (EXCEPT Shockwave Flash

I always wondered why my plugin list displayed certain entries I never added explicitly. It turns out it is due to Firefox's automatic scan for common plugins:

- *Added* Echofon (aka TwitterFox)

- *Changed* Remember browsing history to (at least 30 days)
- *Disabled* Remember search and form history
- *Enabled* Clear history when Firefox closes: everything

- *Changed* Cache settings to use up to 30MB
Reason: Sometime after upgrading to FF3+, I noticed the autosuggest feature in the location bar had changed. It got progressively slower over time. So I am experimenting with a lower history threshold to see how it performs (before taking the extreme step of disabling it entirely).

- *Disabled* Java (No point enabling it since I disabled the plugin)

- *Changed* Show a blank page

- *Changed* When updates to Firefox are found: (Ask me what I want to do)

So far Firefox is doing a lot better. Definitely faster, though maybe not quite as fast as Chrome. At one point I had 15 tabs open in Firefox (only one with Flash Content). Firefox peaked at around 160-180M memory usage in the windows Task Manager and still remained very responsive.

Now to start adding back a plugin at a time, and wait for ugly things to happen. Though I have to say I am enjoying reading some of the more creative ways people express their displeasure with the latest versions of Firefox.

Update November 19, 2009: I am now experimenting with the Firefox 3.6b3 beta and also a plugin called AFOM 2.0 for memory management. (Note: I do not know if this plugin officially supports 3.6b3 yet). But the results from my limited testing seem very promising. (Windows only)

Update November 27, 2009: While the plugin helps, there are still issues. I think it is time to declare defeat. The memory problem is definitely Firefox!

...Read More

Monday, November 16, 2009

CFBarbecue.cfc (...Just because)

While experimenting with the Barbecue library last week, I put together a cfc for my testing. (You knew that was coming. It is the old developer story: read, test, build something, re-test). Anyway, I decided to post it for anyone else out there experimenting with Barbecue. The cfc can generate any of the 25+ barcodes Barbecue handles, with a full range of settings. (Rather than repeat the same code 25+ times, the cfc uses reflection)

Keep in mind the Barbecue library also includes servlet, which is extremely easy to use. Since I was using the built-in webserver at the time, the greatest challenge for me was figuring out how to add a servlet. Not surprisingly I did not find much in the way of documentation. But once I finally got it working, the servlet was a snap to use. Just a simple call to a url:

Download CFBarbecue.cfc (See the download widget on the right menu)

November 19, 2009: Updated source to include missing lib\jdom.jar. Note: This jar is only needed if you are using the JavaLoader and want SVG functionality.

...Read More

Sunday, November 15, 2009

The Wrong Way to Retrieve the Last Record ID Inserted

Hardly a week goes by without seeing some forum post that either uses or recommends my pet peeve: the erroneous SELECT MAX(id) query. Mind you this is across the board, not just in CF. For some bizarre reason, this faulty technique never seems to die. And it REALLY should. There are so many correct alternatives, I cannot understand why anyone would still use it for new code. If you are even considering it, might I inject a note of sanity and recommend some excellent reading material on Adrian J. Moreno's blog:

(His comments on the topic are much more educational and polite than mine ;-)

...Read More

Friday, November 13, 2009

Everything CFDBINFO Never Told You About Your Database (Connection Metadata)

I was doing some debugging and came across the nifty java.sql.DatbaseMetaData class. It is an interface implemented by the various database drivers and it provides you with a ton of information about both your database and driver capabilities. While you can glean some of the information from <cfdbinfo>, DatbaseMetaData tells you so much more.

To use it, you need to open a database connection first. If you have full server access, the simplest method is using the internal ServiceFactory. But you could just as easily open your own jdbc connection. Then call one of the 100+ methods to discover things you may have always wondered about. For example:

  • What database or driver version you are using?
  • What is the maximum number of concurrent connections?
  • What is the maximum number of tables in an ORDER BY?
  • What happens when you concatenate a NULL value with a non-NULL value?

Apparently, the DatabaseMetaData class can tell you that. While I would not take all of these values "as gospel", they do provide some solid guidelines.
myDSN = "MSSQL_2005";

// open a connection to the datasource
factory = createObject("java", "coldfusion.server.ServiceFactory");
datasource = factory.getDataSourceService().getDataSource( myDSN );
connection = datasource.getConnection();
// extract the database metadata
metadata = connection.getMetaData();

results = {};

// Display version information
results["getJDBCMajorVersion()"] = metadata.getJDBCMajorVersion();
results["getJDBCMinorVersion()"] = metadata.getJDBCMinorVersion();
results["getDriverMinorVersion()"] = metadata.getDriverMinorVersion();
results["getDriverName()"] = metadata.getDriverName();
results["getDriverVersion()"] = metadata.getDriverVersion();
results["getDatabaseMajorVersion()"] = metadata.getDatabaseMajorVersion();
results["getDatabaseMinorVersion()"] = metadata.getDatabaseMinorVersion();
results["getDatabaseProductName()"] = metadata.getDatabaseProductName();
results["getDatabaseProductVersion()"] = metadata.getDatabaseProductVersion();

// cleanup

<cfdump var="#results#" label="[#myDSN#] java.sql.DatbaseMetaData" />
As there are so many functions, I used a bit of reflection to quickly dump all of the simple properties into a structure. (See the full code at the end of the entry). It was interesting to see how the metadata differed across drivers. Both in what information was exposed and the results of the core methods. Notice the sql keywords differ when using the JTDS driver and the built-in CF8 driver.

Note: Unfortunately, getSQLKeywords() only returns potential keywords that are not also ansi sql:2003 reserved words. But with a little effort you could easily create a function to list of all potential keywords.

CF8 Driver

JTDS Driver

The metadata contains all kinds of interesting information. So if you ever wondered about your database or driver capabilities, read up on java.sql.DatbaseMetaData. What it reveals could surprise you.

Full Code

myDSN = "MSSQL_2005";

// open a connection to the datasource
factory = createObject("java", "coldfusion.server.ServiceFactory");
datasource = factory.getDataSourceService().getDataSource( myDSN );
connection = datasource.getConnection();
// extract the database metadata
connMetadata = connection.getMetaData();

// get all methods in the metadata class
allMethods = connMetadata.getClass().getDeclaredMethods();
Modifier = createObject("java", "java.lang.reflect.Modifier");

results = {};

// Execute all simple methods and store the results
for(x=1; x <= arrayLen(allMethods); x++)
method = allMethods[x];

// Get the parameters for the current method
parameters = method.getParameterTypes();

// If this is a simple method, requiring no extra information ..
if (Modifier.isPublic(method.getModifiers()))
if (not arrayLen(parameters) AND method.getName() neq "getConnection")
// run the current method
data = method.invoke( connMetadata, parameters );
// convert resultsets to a query object first
if ( isInstanceOf(data, "java.sql.ResultSet")) {
data = createObject("java", "coldfusion.sql.QueryTable").init(data);
results[method.getName()] = data;
catch(java.lang.Exception e)
results[method.getName()] = e.message;

connection.close() ;

<cfdump var="#results#" label="connection.getMetaData() " />

...Read More

Thursday, November 12, 2009

MS SQL: Useless Function Trivia

While reviewing some MS SQL 2005 metadata, I stumbled across something I never noticed before. It still supports the old ODBC "{fn}" functions. With the exception of CurDate and CurTime, there is probably not much need for them. (Nor do I find the syntax particularly appealing ...). But still, it was surprising to find they do still work. So there is my MS SQL oddity for the day.

<cfquery name="q" datasource="MSSQL_2005">
{fn CurDate()} AS [CurrentDate],
{fn CurTime()} AS [CurrentTime],
{fn DayName( getDate() )} AS [DayName],
{fn DayOfMonth( getDate() )} AS [DayOfMonth],
{fn DayOfWeek (getDate() )} AS [DayOfWeek],
{fn DayOfYear(getDate() )} AS [DayOfYear],
{fn Extract(Day From getDate())} AS [ExtractDay],
{fn Hour(getDate() )} AS [Hour],
{fn Minute(getDate() )} AS [Minute],
{fn Month(getDate() )} AS [Month],
{fn MonthName(getDate() )} AS [MonthName],
{fn Now()} AS [Now],
{fn Quarter(getDate() )} AS [Quarter],
{fn Second(getDate() )} AS [Second],
{fn Week(getDate() )} AS [Week],
{fn Year(getDate() )} AS [Year],
{fn TimestampAdd(SQL_TSI_DAY, 14, getDate())} AS [TimestampAdd],
{fn TimestampDiff(SQL_TSI_DAY, '2009-10-31', getDate())} AS [TimestampDiff]
<cfdump var="#q#" label="Old MS SQL Functions fn{}" />

...Read More

OT: Lame error messages

So I was having fun, trying to break things, and came across a lame excuse only a computer could get away with:

com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: memory exhausted.

I can see it now: "Sorry Boss-man. No can do. Geez, my memory is exhausted. I need time to relax, recharge, reboot. Check back with me tomorrow."

...Read More

Sunday, November 8, 2009

ColdFusion: Introduction to the Java Barbecue Library (For Beginners)

Just to get this out of the way: if you are looking for a CF wrapper for the Barbecue library, this is not the entry for you. If you are interested in the basics of using Barbecue, feel free to read on.

Why write yet another entry on Barbecue? Well, I was recently reading about barcodes, for my own edification, and did not find many introductory resources (you know ... ones that ask and answer those silly novice questions?). So decided to write up my findings and first impressions, in the hopes of providing someone else with a starting point in their research. Even if they are not using CF.

Background on Barcodes
Obviously using the library requires you know at least a little bit about the different bar code types. For example, some encode numeric digits only, others have different limits as to how many characters they can encode, etcetera. Two of the more helpful references I came across were: Different Types of Barcodes and a high level Barcode Comparison Chart.

Installing the Barbecue Library
Using the Barbecue library is very simple. Just download the library from and extract the main jar from the .zip file. The current jar version is: barbecue-1.5-beta1.jar. Then place the main jar somewhere in your CF classpath, such as: C:\ColdFusion8\wwwroot\WEB-INF\lib. Finally restart the ColdFusion server so it will detect the jar.

Another option is to use Mark Mandel's JavaLoader.cfc, which is the option I chose. Just load the Barbecue jar in your array of paths, and create a new instance of the JavaLoader. (Obviously, this is just for demonstration. Usually you store the javaLoader instance in the server scope to avoid memory leaks).

Note: The jdom.jar must also be added to the JavaLoader paths if you intend to use Barbecue's SVG functionality.

<cfset paths = [] />
<cfset arrayAppend(paths, expandPath('barbecue-1.5-beta1/barbecue-1.5-beta1.jar')) />
<cfset arrayAppend(paths, expandPath('barbecue-1.5-beta1/lib/jdom.jar')) />
<cfset loader = createObject("component", "javaloader.JavaLoader").init( paths ) />

Factory Workers: (So much for progress..)
I found the easiest way to work with bar codes was using the factory classes. The factories provide easy to use static methods that make it a breeze to create, format and save barcodes.

Creating a Barcode
To a create a barcode, you use the BarcodeFactory. This class contains about 25+ methods for creating several different types of barcodes, such as Interleaved 2 or 5, EAN128, etcetera. Currently, only one 2d bacode is supported: PDF417. The methods are intuitively named, and most require only the string to be encoded. For example, to create a Bookland barcode, just grab a reference to the Factory and call the createBookland() method with the value you wish to encode:

<cfset factory = loader.create("net.sourceforge.barbecue.BarcodeFactory") />
<cfset barcode = factory.createBookland( "0123456789" ) />

Creating a Barcode Image (Simple)

Now the object returned from the BarCodeFactory is not image. It is an instance of one of the Barbecue classes: net.sourceforge.barbecue.Barcode. But there are several easy ways for converting the barcode object into an image. I found at least five (5) methods. (There is a catch to a few of them, but more about that in a minute) .

The first three (3) options use the BarcodeImageHandler factory. It has a few handy methods for converting a barcode object to either an in memory image or to a physical file.

(1) Generating a BufferedImage
To convert a barcode object to a BufferedImage, simply grab a reference to the factory, then call the getImage() method. To display or manipulate the BufferedImage in CF, just wrap it in a CF image object.
<cfset ImageHandler = loader.create("net.sourceforge.barbecue.BarcodeImageHandler") />
<cfset buffImage = ImageHandler.getImage( barcode ) />
<cfset barcodeImage = ImageNew(buffImage) />
<cfimage action="writeToBrowser" source="#barcodeImage#" />
(2) Writing Images to an OutputStream
You can also write the image directly to an OutputStream. There are various uses of OuputStream's, such as streaming images to the browser. There is probably less need for that particular technique in later versions of CF. But as there are several different types of streams, it is good to be aware of this option.

The image handler has three (3) methods for writing images to a stream: writePNG(), writeGIF() and writeJPG(). As their names imply, they generate an image in the desired format and write the image bytes to the supplied stream. You can then use the stream however you wish:

<cfset ImageHandler = loader.create("net.sourceforge.barbecue.BarcodeImageHandler") />
<cfset outStream = loader.create("").init() />
<cfset ImageHandler.writePNG( barcode, outStream ) />
<!--- 1) Display the image using cfimage ... --->
<cfset barcodeImage = ImageNew(outStream.toByteArray()) />
<cfimage action="writeToBrowser" source="#barcodeImage#" />

<!--- 2) OR .. using cfcontent ... --->
<cfcontent type="image/png" variable="#outStream.toByteArray()#" />

(3) Saving Images to a File
Lastly, the handler has three methods for saving barcode images to a physical file: saveGIF(), saveJPG() and savePNG(). It is much like the previous option, only you pass in a object representing the output file.

<cfset ImageHandler = loader.create("net.sourceforge.barbecue.BarcodeImageHandler") />
<cfset IOFile = loader.create("") />

<!--- save the barcode in three (3) different formats  --->
<cfset imageFile = IOFile.init( ExpandPath("myBarcode.gif") ) />
<cfset ImageHandler.saveGIF( barcode, imageFile ) />
<img src="myBarcode.gif" alt="GIF  Format" />

Now earlier I mentioned a catch. In my tests, the generated images all had a black border along the bottom of the barcode. I am guessing it has something to do with the canvas color of the image object. One way I was able to remove it, was by creating a blank image first. So I could control the canvas color. Then use some lower level methods to draw the barcode onto my image. That brings me to the other two (2) options for creating images.

Creating a Barcode Image (Advanced)

While not as easy as using the factory methods, you can also use two methods within the Barcode class itself to generate an image. But unlike using the image factory, it is strictly B.Y.O. (bring your own) .. image. You must already have a blank image in order to use these methods. They are probably what the BarcodeImageFactory uses behind the scenes. However, I thought I would point them out to explain how they work.

1) Using Barcode.draw(graphics, x, y)
The first, and simpler, method is: draw(). To use it, I just created a blank CF image. Since the default canvas color for "rgb" images is black, I changed it to white. Then extracted the graphics object from my image and passed it into the draw function. The result was a barcode without that bottom border.

<!--- Option 1: RBG image, white canvas --->
<cfset img = ImageNew("", barcode.getWidth(), barcode.getHeight(), "rgb", "ffffff") />
<cfset graphics = ImageGetBufferedImage(img).getGraphics() />
<cfset barcode.draw( graphics, 0, 0) />

<cfimage action="writeToBrowser" source="#img#">
<br /><br />

<!--- Option 2: Transparent image --->
<cfset img = ImageNew("", barcode.getWidth(), barcode.getHeight(), "argb") />
<cfset graphics = ImageGetBufferedImage(img).getGraphics() />
<cfset barcode.draw( graphics, 0, 0) />

<div style="background-color: blue;">
<cfimage action="writeToBrowser" source="#img#"> <br />

2) Using Barcode.output(output)
The second method is a little more complex. You probably will not need it. But still, it is good to know. The output() method works with an instance of what Barbecue calls an Output object. The are several types of Output classes, and they are all just used to convert Barcode objects from one format to another: like to image, svg or eps.

Since I wanted to create an image, I used the GraphicsOutput class. It is not as complicated as it looks, but it does involve more moving pieces than other options. Constructing a GraphicsOutput object requires several pieces of information: the image graphics, barcode font and fore/background colors. You simply pass these objects into the constructor to create a new Output object. Then call Barcode.output() to paint the barcode onto your image graphics using the supplied settings.



<!--- create a new image and extract the graphics --->
<cfset img = ImageNew("", barcode.getWidth(), barcode.getHeight(), "rgb", "ffffff") />
<cfset graphics = ImageGetBufferedImage(img).getGraphics() />

<!--- create the desired font and colors --->
<cfset Font = createObject("java", "java.awt.Font") />
<cfset Color = createObject("java", "java.awt.Color") />

<cfset barcodeFont = Font.init("Arial", Font.PLAIN, 12 ) />
<cfset backgroundColor = Color.BLUE />
<cfset foregroundColor = Color.decode("##008040") />

<!--- grab a Color object that will be used to set the barcode fore/background --->
<cfset GraphicsOutput = loader.create("net.sourceforge.barbecue.output.GraphicsOutput")>
<cfset gOut = GraphicsOutput.init( graphics, barcodeFont, foregroundColor, backgroundColor ) />
<cfset barcode.output( gOut ) />

<cfset ImageWrite( img, ExpandPath("ColdFusion_Barbecue_GraphicsOutput.png")) />
<!--- display the barcode image --->
<cfimage action="writeToBrowser" source="#img#"> <br />

Working with Barcode Properties

Working with the various ways of creating images eventually brought me back to the Barcode class itself, and using its methods to alter some of the default settings. One of the things that struck me about the Barcode class is that it extends JComponent. At first that seemed a bit strange, as JComponent is one of the base classes for swing gui components. But further reading revealed Barbecue can be used in servlets or swing applications. So extending JComponent made more sense.

Now I mentioned swing for a reason. After testing various Barcode properties, I quickly realized many of the properties only apply if you are using swing. For example, there are several methods for adjusting width and height. But they had no effect when using images. Considering these methods are inherited from JComponent, that makes sense. (Not that you can really force the barcode dimensions anyway). An entry on Adam Presley's blog confirmed my suspicion. You cannot set the dimensions explicitly. But instead should use the barWidth and barHeight properties to adjust the overall barcode size.

Here are some of the more useful Barcode properties.

These methods can be used to set the width of the thinnest bar or the height of a single bar, within the overall barcode. You may need to experiment to get the correct settings. So you do not end up with an invalid barcode or one that does not render properly ;)

setBarWidth( 6 )


<cfset BarcodeFactory = loader.create("net.sourceforge.barbecue.BarcodeFactory") />
<cfset barcode = BarcodeFactory.createStd2of5( "0123456789" ) />
<cfset barcode.setBarWidth( 6 ) />

This setting controls whether extra whitespace (ie quiet zone) should be rendered.


	<cfset barcode.setDrawingQuietSection( false ) />



This setting controls whether the barcode data (ie un-encoded) is displayed beneath the barcode.


	<cfset barcode.setDrawingText( false ) />

This method should allow you to override the text displayed beneath the barcode. But I was not able to get this setting to work.

This method allows you to use a different font for any text displayed beneath the barcode.


<cfset Font = createObject("java", "java.awt.Font") />
<cfset barcodeFont = Font.init("Arial", Font.ITALIC, 12) />
<cfset barcode.setFont( barcodeFont ) />
setFont( [Arial, Italic, size 12] )

These methods can be used to change the foreground or background colors of the barcode. They are inherited from JComponent, but are used by the Barcode class.


<!--- change foreground --->
<cfset Color = createObject("java", "java.awt.Color") />
<cfset barcode.setForeground( Color.RED ) />

<!--- change background (hex color) --->
<cfset Color = createObject("java", "java.awt.Color") />
<cfset barcode.setBackground( Color.decode("##0080c0") ) />



Used to adjust the resolution. Default is 72dpi.

	<cfset barcode.setResolution( 300 ) />
You can also convert barcode objects to svg. Simply grab a reference to the SVGFormatter class, then call the formatAsSVG() method. The result is an svg string.

<cfset BarcodeFactory = loader.create("net.sourceforge.barbecue.BarcodeFactory") />
<cfset barcode = BarcodeFactory.createBookland( "0123456789" ) />
<cfset SVGFormatter = loader.create("net.sourceforge.barbecue.formatter.SVGFormatter") />
<cfset svgString = SVGFormatter.formatAsSVG( barcode )>
Related Entries:
CFBarbecue.cfc (...Just because)
Seeing Stripes: Experiment with ZXing

...Read More

Saturday, November 7, 2009

OT: ColdFusion Trials (Let's Do The Time Warp Again)

I was checking my "trials" email folder the other day and these messages gave me a chuckle. That and the fact that I received a "Your ColdFusion 9 trial starts now" message, before my download had even finished! Can you spot what is wrong here?

Yes... I did download the ColdFusion 9 Trial previously. But it still made me laugh ;)

...Read More

ColdFusion: Small Try/Catch Gotcha With CreateObject() and Exceptions

Exceptions can be deceptive and loathsome creatures at times. Case in point, I was happily using createObject() to load an object from a java jar in my classpath. I then added a try/catch. So I could detect when the jar probably was not added to the CF classpath properly, and display a more meaningful error message.

handler = createObject("java", "net.sourceforge.barbecue.BarcodeImageHandler");
catch(java.lang.ClassNotFoundException e)
throwError(message="Verify the Barbecue jar is in your CF classpath", type="SupportingClassNotFound");

Great in theory, the only problem was my try/catch did not work. I double checked the stack trace and it sure seemed like I was catching the correct exception type: java.lang.ClassNotFoundException. So I tried the broader type java.lang.Exception. Then cfdump'd the details, to see if I could spot the problem. Sure enough, the actual exception type was not what CF was reporting on the error screen. The actual exception type being:

(Boy, what a mouthful. As if the base exception name is not long enough already...?)

Apparently the stack trace in the error page was displaying the RootCause, not the actual exception thrown. While that makes some sense, unfortunately the thrown exception type is what is needed to catch the darned thing. Anyway, once I changed my try/catch to use the actual exception type being thrown, everything was just dandy.
handler = createObject("java", "net.sourceforge.barbecue.BarcodeImageHandler");
catch( e)
throwError(message="Verify the Barbecue jar is in your CF classpath", type="SupportingClassNotFound");
What is the world coming to when you cannot even trust error messages Oh, wait. Scratch that. Error messages lie all the time. I am sure they do not mean to lie, they just hold back critical information when you most need it. The secretive little trolls ;)

...Read More

No more use for StructFind() ?

For some reason I always forget about the one good use for StructFind(). (Thank goodness for old blog entries). Though it seems even that use is gone in ColdFusion 9. Can anyone think of another one?

<!--- Works in CF9, not CF8 --->

<cffunction name="getSomeStructure" returntype="struct">
<cfset var myStruct = {} />
<cfset = "felix" />
<cfset = "clifford" />
<cfreturn myStruct />

...Read More

Tuesday, November 3, 2009

A Better PdfPageEventHandler with JavaLoader/CFCDynamicProxy

Yesterday , I wrote about discovering a true gem in latest version of Mark Mandel's JavaLoader: the CFCDynamicProxy. This tiny, but powerful, class acts as a wrapper around a ColdFusion cfc. Essentially allowing it to mimic a native java object and communicate directly with other java objects in ways it never could before! We all know just about everything in ColdFusion boils down to a java object internally. But until now there were certain things you just could not do from a cfc without taking the r-e-a-l-l-y long way around. Well, the CFCDynamicProxy changes all that.

A perfect example is page events in iText. In order to add custom headers, footers, etcetera with iText you need a custom java class that implements the PdfPageEvent interface. Well as easy and natural as that is in a java environment, it is a bit awkward to have to create and load a new java class in CF every time you wish to create a different set of headers or footers.

Eventually, I came up with a more dynamic method of calling a cffunction to add headers/footers from a java, but it still required that custom java class. But using the CFCDynamicProxy, I was able to simplify and rewrite the code entirely in CF, and let me say... man what a difference! It is now totally dynamic and written in native CF, no extra java classes needed. In comparison to the elegance of using the new Proxy, my previous attempt looks like a decrepit, wart-nosed-hag. To be fair, a lot of what the previous entry was doing manually is still going on in the background. But
the CFCDynamicProxy makes it much, much simpler.

The first step in my code beautification effort was creating a CFC that implemented all of the methods in the iText PdfPageEvent interface. Just as you would if you were implementing a CF interface. Using the PdfPageEvent API, I created a cfc with a function for each of the methods in that interface, taking care to properly align the arguments and data types so they matched their java counterparts.

Mapping the java to ColdFusion data types is pretty simple. Any arguments that are instances of a java class (like PdfWriter, Document, Rectangle) are all mapped to type="any". The rest are usually primitive types like "string" and "float" and the conversions are intuitive, for the most part.

Next I filled in a few functions needed for generating basic page footers. The key function was onPageEnd, which is where all the action takes place. OnPageEnd will be called by iText just before new pages are written to my new pdf document. So inside that function, I grab the writer object and use it to set the desired properties and finally write the footer text onto the pdf.

<cffunction name="onEndPage" access="public" returntype="void" output="true"
hint="Called when a page is finished, just before being written to the document.">
<cfargument name="writer" type="any" required="true" hint="Writer for the target pdf. Instance of com.lowagie.text.pdf.PdfWriter" />
<cfargument name="document" type="any" required="true" hint="Document for target pdf. Instance of com.lowagie.text.Document" />
<cfset var Local = {} />

if (len(variables.instance.footerText))
Local.cb = arguments.writer.getDirectContent();

Local.cb.setColorFill( variables.instance.textColor );
Local.cb.setFontAndSize( variables.instance.font, javacast("float", variables.instance.fontSize) );
Local.cb.setTextMatrix( arguments.document.left(), arguments.document.bottom() - 10);
Local.text = variables.instance.footerText &" page ["& arguments.writer.getPageNumber() &"]";
Local.cb.showText( Local.text );



Having completed my faux-java-class/cfc, the next step was using the CFCDynamicProxy to see if this stuff actually worked. Using the Dynamic proxy is like using any other jar with the JavaLoader. You just add the new cfcdynamicproxy.jar to the array of paths. Then create an instance of the JavaLoader, except this time you set the loadColdFusionClassPath parameter equal to true. This allows the JavaLoader to access ColdFusion's classes to create the proxy object.

Note: The code example included with JavaLoader 1.0 was very easy to follow. So my base code is ripped straight out of Mark's example.

//add the javaloader dynamic proxy library (and the iText jar) to the javaloader
libpaths = [];
arrayAppend(libpaths, expandPath("/javaLoader/support/cfcdynamicproxy/lib/cfcdynamicproxy.jar"));
arrayAppend(libpaths, expandPath("/dev/itext/iText-2.1.7.jar") );

//we HAVE to load the ColdFusion class path to use the dynamic proxy, as it uses ColdFusion's classes
loader = createObject("component", "javaLoader.JavaLoader").init(loadPaths=libpaths, loadColdFusionClassPath=true);

Once you have an instance of the JavaLoader, instantiate your cfc as usual. (In my case, I am instantiating my PdfPageEventHander.cfc) The last step is to wrap the cfc in a proxy, which was incredibly simple. First you create an array of all of the interfaces, your cfc implements. Then grab a reference to the new Dynamic Proxy class and use its createInstance method to create your proxy object. That is all there is to it.

//intialize the page event handler component
eventHandler = createObject("component", "PdfPageEventHandler").init( font=textFont, fontSize=10, textColor=textColor);
//add a custom footer
eventHandler.setFooterText( "www.clueless.corp * 85 anywhere blvd * lost city" );

//we can pass in an array of strings which name all the interfaces we want out dynamic proxy to implement
interfaces = ["com.lowagie.text.pdf.PdfPageEvent"];

//get a reference to the dynamic proxy class
CFCDynamicProxy = loader.create("com.compoundtheory.coldfusion.cfc.CFCDynamicProxy");

// create a proxy that we will pass to the iText writer
eventHandlerProxy = CFCDynamicProxy.createInstance(eventHandler, interfaces);

Now for the real test. I passed my proxy object into my iText writer object, just as I would if it were a native java class:
// ...
fullPathToOutputFile = ExpandPath("./ABetterPdfPageEventHandler.pdf");
document = loader.create("com.lowagie.text.Document").init();
outStream = createObject("java", "").init(fullPathToOutputFile);
writer = loader.create("com.lowagie.text.pdf.PdfWriter").getInstance(document, outStream);

// ** register the PROXY as the page event handler **
writer.setPageEvent( eventHandlerProxy );;
Paragraph = loader.create("com.lowagie.text.Paragraph");
document.add( Paragraph.init("Paragraph Text....") );
document.add( Paragraph.init("Paragraph Text....") );


.. and iText was none the wiser! Voila, instant footers.

A few simple changes to my cfc and I had instant headers instead. It feels like writing java code ... except in CF ;)

You know when I woke up this morning, I thought all of this might have been just a dream. But CFCDynamicProxy is real .. and this class seriously rocks!

...Read More

JavaLoader 1.0 / CFCDynamicProxy (I Think I am in Love)

So after many weeks, I finally got a chance to work with Mark Mandel's JavaLoader 1.0 Beta today, and boy did it put a smile on my face. When I started reading the documentation on the Dynamic Proxy, I nearly fell out of my chair.

...And there on the other side, read the following words ...

In JavaLoader 1.0, I've written a Dynamic Proxy that you are able to wrap around a CFC, and thereby make Java Objects think they are interacting with a native Java object, but are in fact, talking to your CFC.

Holy cow! If you do any interaction with java from CF (or ever wanted to) I think you can appreciate this phenomenally cool feature. It means you can hook directly into a java library, from a CFC. No need to write and compile a custom java class. Now obviously it only applies to interfaces. But hey, most good libraries "program to the interface and not the implementation" anyway. So this opens up a lot of possibilities.

A good example is adding custom headers and footers with iText. (See this previous entry some background on page events). If you were using java, you would create a custom class that implements the PdfPageEvent interface. By implementing that interface, the custom class can capture certain pdf events like document opened, page added, etcetera. Then the code inside the custom class adds the desired headers, footers, watermarks, etcetera as those events are detected.

Now that is easy enough to do from java. But until now, it was not possible with ColdFusion alone, because iText requires an instance of a java class to handle the page events. Prior to now there were work-arounds, but none as elegant as using the Dynamic Proxy. With the new Proxy, you can simply write the event handler in CF. Then wrap it up in a proxy object and pass the proxy into iText. iText will think it is dealing with a real java class and simply route the events to your cfc as usual.

In a relatively short period of time, I was able to greatly simplify some old code for adding headers and footers to pdf documents using iText. I will post it shortly just so you can see how easy it was to interact with iText using the new Dynamic Proxy. I have to say it feels a lot more natural, and I am seeing endless possibilities here.

I said it before and I will say it again. That Mark Mandel is a freak...'in genius.

...Read More

What the heck is ConvolveOp? (ImageSharpen Alternative)

A recent question on the adobe forums mentioned the ImageSharpen() function was adding a black border to the generated image. As google did not turn up much on the topic, I concluded most people are using alternate methods to sharpen images. A quick peek at some of the more popular image components confirmed it. These components all use java's ConvolveOp class, internally, for sharpening.

So what is ConvolveOp? This article gives a great explanation on how it works, why the border effect occurs and how to avoid it: Java 2D's ConvolveOp Class. If you are already using one of the components above, you probably do not have to worry about it. But if you are curious about how things work, the article makes a good read.

...Read More

Sunday, November 1, 2009

CFThread Must Die (Kill Bill) - Part 2

In Part 1 I mentioned the internal CF class coldfusion.thread.Task that seems to be used for running processes with cfthread. Tasks for a given thread can be retrieved from the FusionContext class using a method called getUserThreadTask(). So between the two, you should have enough information for makings of an experimental tracker.

Now I was particularly interested in the coldfusion.thread.Task's cancel() method and whether it could be used to kill a cfthread programatically. So I created a very simple cfthread. The thread itself does very little besides outputting a few messages. But I added a few sleep statements to simulate a thread that would take twenty (20) seconds to execute.

<cfset threadName = "CFZombieThread_Test" >

<!--- Start the new thread ---->
<cfthread action="run" name="#threadName#" myName="#threadName#">
<cfset var zombieThreadName = getPageContext().getFusionContext().getCurrentUserThreadName() />

<cfloop from="1" to="20" index="counter">

Mindless groan from zombie thread #zombieThreadName# at #now()# ...<br/>
Force the thread to sleep to simulate a long running task
<cfset sleep(1 * 1000) />



Next, I used the ever handy getPageContext() function to grab the FusionContext. I am not extremely familiar with this class. But from what I have learned, it provides executing requests with access to context-sensitive information like session variables, application settings, etcetera. After grabbing the context, I called the getUserThreadTask() method to retrieve the Task associated with my newly created thread.

Note: When using this method be sure to convert the thread name to upper case. I discovered through trial and error that the thread names are stored in some type of case sensitive structure, and all the keys are in upper case. So if you use mixed, or lower case, the method will not be able to locate your thread.

<cfset context = getPageContext().getFusionContext() />
<cfset task = context.getUserThreadTask(UCASE(threadName)) />
<cfdump var="#cfthread[threadName]#" label="#threadName# BEFORE cancel() request " />

Next, I forced the current page to take a brief nap, to give my thread a chance to produce some output and prove it was running. Finally, I used the Task's cancel() method to kill the thread. Then forced the parent page to sleep again before checking the thread's final status. Just to give the thread a chance to catch its breath.

<!--- Sleep for a few seconds to give the thread a chance to produce some output ...--->
<cfset createObject("java", "java.lang.Thread").currentThread().sleep(2000) />

<!--- Now, try and kill the thread --->
<cfset task.cancel() />

<!--- Finally, give the thread a chance to catch up before we check its final status ...--->
<cfset createObject("java", "java.lang.Thread").currentThread().sleep(500) />
<cfdump var="#cfthread[threadName]#" label="#threadName# AFTER cancel() request " />

As you can see, the thread was successfully terminated.

The next step was storing the thread information in a shared scope. So it could be accessed as needed. Now I know there are a few ways you could approach it and different scopes you could use. But for the sake of simplicity, I decided to have the threads add and remove themselves from a tracker object stored the application scope. It is obviously not as elegant as a built in monitor, and could definitely use improvement. But it was good enough for some simple tests.

<!--- Start the new thread ---->
<cfthread action="run" name="#newThreadName#" >

<cfset var myThreadUUID = "" />
<cfset var myThreadContext = getPageContext().getFusionContext() />
<cfset var myThreadName = myThreadContext.getCurrentUserThreadName() />

First, the thread will add itself to the tracker object
<cfset myThreadUUID = application.threadTracker.addThread(
threadName = myThreadName,
threadTask = myThreadContext.getUserThreadTask(myThreadName)
) />


Finally, the finished thread will remove itself from
the application tracker object
<cfset application.threadTracker.removeThread(
threadUUID = myThreadUUID
) />

Now for the Usual Disclaimers....
As there is a bit too much code to post, I threw together a small example to demonstrate the concepts. It is rough and highly experimental. So use it at your own risk ;) Now I have only had a chance to test it on the Developer version. I am assuming it will work on the Standard edition too.

Anyway, for those interested in how it works, you can download the sample code from widget in the right menu. (Absolutely zero time went into window dressing. So if you have delicate eyes, you might want to leave the room now).

...Read More

  © Blogger templates The Professional Template by 2008

Header image adapted from atomicjeep