Friday, July 31, 2009

Installing a Development Mail Server for Testing CFMAIL

Occasionally I need to run some cfmail tests in an environment that does not have access to a mail server, or only a live server I do not want to use for testing. A while back I stumbled across the hMailServer program which is a great tool to have in those situations. Although it can function as a full fledged mail server, it can also be configured to act as a local mail server only. I have not used it in a while, but I found myself needing to install it this week. It is pretty easy to configure. But my memory was a little hazy on a few points. So I thought I would write up some instructions as a reference for next time.

The basic process is very simple. You just install the mail server and create a fake domain with a few email addresses. Then modify the mail server settings to ensure it does not allow messages to be sent to external email addresses (ie the outside world).


<!---
Send message
--->
<cfmail to="memyselfandI@psuedo-yahoo.com"
from="cfsearching@psuedo-yahoo.com"
subject="Just Because"
type="html">
<h1>This is a Test</h1>
It is only a test. Had this been a real email,
there would be meaningful content here.
</cfmail>

<!---
Retrieve message
--->
<cfpop action="getAll"
server="127.0.0.1"
username="memyselfandi@psuedo-yahoo.com"
password="#thePassword#"
name="getMail"
/>

<cfdump var="#getMail#"
label="Messages for memyselfandI@psuedo-yahoo.com" />





Obviously there is much more to mail server configuration, but that should be enough to get you started. So if you ever need a development only mail server on windows, consider giving hMailServer a try.

...Read More

Tuesday, July 28, 2009

OT: Food for Thought

Just some food for thought:

The Spirit of Learning

Monday, July 27, 2009

ColdFusion: Basic example - Positioning buttons with iText

Recently, a poster named Brook asked about positioning PushButtonFields with iText. Positioning is relatively simple once you understand the default coordinate system. However, if you are like me, that may initially require a slight adjustment in thinking. Unlike top-down systems, the pdf coordinates work from the bottom up, where 0,0 signifies the lower left corner. Also, the default measurement is in points. So you must take both of these things into account in order to position elements.


<cfscript>
box = Rectangle.init( document.right()-72, // llx = lower left x position
document.bottom(), // lly = lower left y position
document.right(), // urx = upper right x position
document.bottom() + 36 // ury = upper right y position
);
// ...
</cfscript>


If you look at the blue angular brackets in the image below, you can easily see how these values combine to determine both size and position of the buttons.



Note, this example uses the JavaLoader.cfc to load a newer version of iText.

<cfscript>
javaLoader = server[MyUniqueKeyForJavaLoader];
fullPathToOutputFile = ExpandPath("./Buttonapalooza.pdf");

PageSize = javaLoader.create("com.lowagie.text.PageSize");
document = javaLoader.create("com.lowagie.text.Document").init(PageSize.LETTER);
outStream = javaLoader.create("java.io.FileOutputStream").init(fullPathToOutputFile);
writer = javaLoader.create("com.lowagie.text.pdf.PdfWriter").getInstance(document, outStream);
Color = javaLoader.create("java.awt.Color");

PushbuttonField = javaloader.create("com.lowagie.text.pdf.PushbuttonField");
Rectangle = javaloader.create("com.lowagie.text.Rectangle");
// the coordinates define both position and size of the field
box = Rectangle.init( document.right()-72, // lower left
document.bottom(), // lower right
document.right(), // upper x
document.bottom() + 36 // upper y
);

document.open();
for (i = 1; i lte 5; i = i+1)
{
button = PushbuttonField.init(writer, box, "buttonField"& i);
button.setText("Button "& i);
button.setBackgroundColor(Color.GRAY);
document.newPage();
writer.addAnnotation(button.getField());
}

document.close();
WriteOutput("Finished!");
</cfscript>


Related articles

...Read More

Saturday, July 25, 2009

ColdFusion 8: Experiment with compiling Java code from ColdFusion - Part 2

In Part 1 I described how you can use JavaCompiler's run() method to compile source code from a java.io.File. However the api also describes how you might extend the SimpleJavaFileObject class so you can compile source code from a string, rather than a physical file.

Since you can now compile code on-the-fly, you can easily use the CF code from part 1 to create their sample class: JavaSourceFromString.


<cfsavecontent variable="javaCode">
import javax.tools.SimpleJavaFileObject;
import java.net.URI;

/**
* A file object used to represent source coming from a string.
*/
public class JavaSourceFromString extends SimpleJavaFileObject {
/**
* The source code of this "file".
*/
final String code;

/**
* Constructs a new JavaSourceFromString.
* @param name the name of the compilation unit represented by this file object
* @param code the source code for the compilation unit represented by this file object
*/
public JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),
Kind.SOURCE);
this.code = code;
}

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
</cfsavecontent>


<cfset pathToInputFile = "C:\myFiles\JavaSourceFromString.java" />
<cfset fileWrite(pathToInputFile, javaCode) />

However, instead of using the JavaLoader this time, we are going to output the class file directly to the WEB-INF\classes\ directory. That way the class will be automatically detected by ColdFusion. Now to redirect the output of the compiler, simply prepend the -d <directory> flag to the list of sources passed into the compiler.


<!--- create a stream to capture errors --->
<cfset errStream = createObject("java", "java.io.ByteArrayOutputStream").init() />

<!--- get a compiler reference --->
<cfset provider = createObject("java", "javax.tools.ToolProvider") />
<cfset compiler = provider.getSystemJavaCompiler() />

<cfset args = [ "-d", "C:\ColdFusion8\wwwroot\WEB-INF\classes\", pathToInputFile ]>
<cfset status = compiler.run( javacast("null", ""),
javacast("null", ""),
errStream,
args
) />

<!--- display the status and any error messages --->
<!--- status: 0 == success --->
<cfset message = toString(errStream.toByteArray()) />
<cfset errStream.close() />
<b>COMPILER RESULTS:</b><hr />
<cfoutput>
STATUS: #status# (0 == success)<br />
ERRORS: #message# <br /><br />
</cfoutput>

Once the code is compiled, ColdFusion should automatically detect the new .class file in WEB-INF\classes\. So if everything went as planned, the new class should be ready to use right away, without rebooting the server. (Note: If you make changes and compile the class a second time, ColdFusion will not automatically reload the class).

Using the new class is very simple. Just create a new instance of JavaSourceFromString, and pass in the name of your class and the source code as strings.

<cfsavecontent variable="javaCode">
import java.util.List;
import java.util.Vector;
public class MyClass
{
public static List<String> test() {
List<String> list = new Vector<String>();
list.add("Congratulations");
list.add("It worked");
return list;
}
}
</cfsavecontent>

<cfset SourceFromString = createObject("java", "JavaSourceFromString") />
<cfset fileObject = SourceFromString.init( "MyClass", javaCode) />
<cfset sourceList = [ fileObject ] />


Now to compile these sources, we are going to try something a little different: using a CompilationTask. This option just provides some additional control over the compile process. We are also going to use a DiagnosticCollector object, which provides an easy way to access error message details.

You can create a CompilationTask using the compiler's getTask() method. Most of the arguments are the same as for the run() method. But there are two additional arguments: options and classNames. The options argument is just an array of settings you want to pass to the compiler. Such as the -d flag used in the previous example. We are going to use that flag again to send the output to the WEB-INF\classes directory. Now to perform the actual compilation, use the call() method.


<cfset provider = createObject("java", "javax.tools.ToolProvider") />
<cfset compiler = provider.getSystemJavaCompiler() />

<cfset collector = createObject("java", "javax.tools.DiagnosticCollector").init() />
<cfset args = ["-d", "C:\ColdFusion8\wwwroot\WEB-INF\classes\" ]>
<cfset task = compiler.getTask( javacast("null", ""),
javacast("null", ""),
collector,
args,
javacast("null", ""),
sourceList
) />
<cfset status = task.call() />


To retrieve any error messages from the DiagnosticCollector, simply call the getDiagnostics() method. It returns an array of Diagnostic objects.


<cfset results = collector.getDiagnostics() />
COMPILER RESULTS:<hr />
<cfoutput>
STATUS: #status# (0 == success) <br />
<cfloop array="#results#" index="diagnostic">
Error on #diagnostic.getLineNumber()#
Starting at #diagnostic.getStartPosition()# <hr />
#diagnostic.getMessage(javacast("null", ""))# <br />
</cfloop>
<br />
</cfoutput>


All that is left is to test the compiled class. If everything went well, your results should look something like this. Ah, cfdump has never looked better ;)


COLDFUSION RESULTS:<hr />
<cfset myObj = createObject("java", "MyClass") />
<cfdump var="#myObj.test()#" label="MyClass.test();" />




As always comments/questions/corrections are welcome.

...Read More

ColdFusion 8: Experiment with compiling Java code from ColdFusion - Part 1

A few months back I was looking through the ColdFusion8\lib directory and noticed the tools.jar file which contained some intriguingly named classes. Needless to say I did not get around to exploring it then, but I finally did get back to it last weekend for a time.

Gold Rush
Now what caught my eye was a class named sun.tools.javac . If you have ever compiled a java class you will undoubtedly recognize the name. For those that have not, javac.exe is a small program used to compile java code from the command line and sun.tools.javac is a java class that allows you to do the same thing, only programatically. So the class definitely has some interesting potential.

  • The sun.tools.javac class is internal and undocumented
  • The sun.tools.javac class was deprecated around version 1.4
  • When I tried using sun.tools.javac from ColdFusion 8, it did compile my source code. But it also bounced the CF Server in the process. (Granted I only ran a few cursory tests after discovering it was undocumented.)

Back to the drawing board I went.

The Real McCoy
Eventually I found the JavaCompiler class, which is an officially supported replacement for sun.tools.javac. The JavaCompiler was introduced in jdk 1.6. (Note the jdk reference. To use the JavaCompiler you must be running a jdk, not just a jre.) As with many java classes, JavaCompiler is designed to be extended so developers can easily add additional functionality. But you can still use the default implementation from ColdFusion, without having to build a custom java class. Fortunately an article on openjdk.com helped fill in some of the gaps in the main api.

Tool Time
Now to use the JavaCompiler I obviously needed some java code. So I created a rather pointless "Clock" class and saved the code to a file on disk. Note, you can use whatever directory you wish, but the file name must match the class name ie use "Clock.java".
<cfsavecontent variable="sourceCode">
import java.util.Date;
public class Clock
{
public Clock()
{
}

public String tick() {
return "tick tock it is: "+ new Date();
}
}
</cfsavecontent>

<cfset pathToSourceFile = "c:\myFiles\Clock.java" />
<cfset fileWrite(pathToSourceFile, sourceCode)>

Next, I grabbed a reference to the system compiler from the ToolProvider class. To compile the code I used the simpler JavaCompiler.run() method, which accepts several arguments. As the openJDK article describes:

The first three arguments to this method can be used to specify alternative streams to System.in, System.out, and System.err. The first two arguments are ignored by the compiler and the last one control where the output from -help and diagnostic messages will go.

So I first created output stream to capture any error messages. Next I created an array of the .java files I wanted to compile. Finally, I called the run() method to actually compile the java code. The result was a brand new .class file located in the same directory as my source file (ie c:\myFiles\ClockClass.class)

<cfset provider = createObject("java", "javax.tools.ToolProvider")>
<cfset compiler = provider.getSystemJavaCompiler()>

<cfset errStream = createObject("java", "java.io.ByteArrayOutputStream").init()>
<cfset args = [ pathToSourceFile ]>
<cfset status = compiler.run( javacast("null", ""),
javacast("null", ""),
errStream,
args
)>

<cfset message = toString(errStream.toByteArray())>
<cfset errStream.close()>
COMPILER RESULTS:<hr>
<cfoutput>
STATUS: #status# (0 == success) <br />
ERRORS: #message# <br />
</cfoutput>

(Drum Roll Please)
All that was left was to test the .class file. For this task I chose to use the JavaLoader.cfc. In its present incarnation, the JavaLoader cannot load individual class files. At least as far as I know. So I used the handy <cfzip> tag to add my new class file to a jar. Finally, I loaded my jar into the JavaLoader and voila, instant results.

Important note: The example below creates a new instance of the JavaLoader on each request for demonstration purposes only. See Using a Java URLClassLoader in CFMX Can Cause a Memory Leak for proper usage tips.

<cfzip action="zip" source="c:\myFiles\" filter="*.class" file="c:\myFiles\clock.jar">
<cfset jarPaths = [ "c:\myFiles\clock.jar" ] >
<cfset javaLoader = createObject("component", "javaLoader.JavaLoader").init(jarPaths)>

<cfset clock = javaLoader.create("Clock").init()>
COLDFUSION RESULTS:<hr>
<cfoutput>
clock.tick() = #clock.tick()# <br/ ><br />
</cfoutput>

<cfdump var="#clock#" label="My Clock class">



The World's Smallest Violin
As JavaCompiler has been around since version 1.6, I had no illusions that I was the first person to discover this tool. Java developers have probably known about it for quite some time. However, I really enjoyed learning about it and how to use it from ColdFusion. For that reason I chose to wait until the end to see if anyone else in the ColdFusion realm had blogged about it. Well, as I went to post this entry last night I was a bit crushed to discover Adrian Walker had just posted something similar two weeks ago. But as our approaches were a bit different, I decided to rise above my disappointment, and post it anyway ;) But seriously, I thought having a bit of extra background might be useful to those interested in using the tool.

So in the vein of "finish what you start" and "but ...{sputter} ... it is already written", more about compiling code with JavaCompiler.CompilationTask in Part 2 ...

...Read More

Thursday, July 23, 2009

SQL Refresher: It is How You Say it That Matters

While growing up, we were all taught that how you say something is just as important as what you say. Imagine if you went into a restaurant and said to the waitress "Fetch me some coffee you lazy slug!". You probably would not receive the coffee for quite some time ( .. and that is a best case scenario). Well, sometimes we forget that databases are no different.

A recent post on cf-talk, by Brad Wood, highlighted exactly this point. He mentioned that using string manipulation functions on a particular column would prevent the proper usage of indexes. Furthermore, it could negatively impact performance as it would likely require a scan of every record in the table. It is a bit like having to read through a two thousand page book looking for a favorite section. You know it is in there somewhere, but without an index you have not a clue where to begin looking for it. So you must resort to reading all of the pages to find it.


--- Create the sample table
CREATE TABLE Widget
(
WidgetID INT IDENTITY(1,1),
WidgetName VARCHAR(150),
DateCreated DATETIME
)
GO

--- Create the indexes
CREATE INDEX idxWidget_WidgetName ON Widget(WidgetName)
GO
CREATE INDEX idxWidget_DateCreated ON Widget(DateCreated, WidgetName)
GO

--- Populate the table
DECLARE @i INT
SET @i = 0

WHILE @i < 60000
BEGIN
INSERT INTO Widget(WidgetName, DateCreated)
VALUES ( 'Widget #'+ CAST(@i AS VARCHAR), DATEADD(d, @i, getDate()) )

SET @i = @i + 1
END
GO


Finally, I created two versions of a query that searched for records where the WidgetName began with the characters "Widget #111%". Now both queries return the exact same information. They just phrase the request a bit differently.


--- TEST QUERIES
SELECT WidgetName
FROM Widget
WHERE WidgetName LIKE 'Widget #111%'

SELECT WidgetName
FROM Widget
WHERE LEFT(WidgetName, 11) = 'Widget #111'


When I viewed the execution plans, it clearly showed that using the LEFT() function caused the database to perform a less efficient index scan. The index scan essentially touches every record in the index to obtain the desired data. Whereas the index seek locates the data with much greater precision. Notice the overall estimated cost of the second query is very high, relative to the first one (99% versus 1%) as well as its i/o costs. Keep in mind the numbers are relative to the overall batch here. But they do provide a good sense of one query's efficiency over another.



Now this does not just apply to string columns. Say you needed to retrieve a listing of all widgets created in the month of July 2009, sorted by name. In this scenario, you might create a covering index on the DateCreated and WidgetName columns. Again, write two similar queries. Both requesting the same data, just in slightly different ways.


--- Queries on Widget Date
SELECT WidgetName
FROM Widget
WHERE MONTH(DateCreated) = 7
AND YEAR(DateCreated) = 2009
ORDER BY WidgetName

SELECT WidgetName
FROM Widget
WHERE DateCreated >= CONVERT(DATETIME, '20090701', 112)
AND DateCreated < CONVERT(DATETIME, '20090801', 112)
ORDER BY WidgetName


Once again, the execution plans show the query without the functions used the more efficient index seek, with the relative costs being %4 versus %96.



Of course there are always exceptions. For example, if a table contained a small amount of data or a query requests a large percentage of the records from a table, the database engine may decide it is more efficient to perform some sort of scan. So ultimately the database engine makes the final decision. But you can certainly point it in the right direction by writing smarter queries.

Now obviously there is a lot more to analyzing query plans than just reviewing high level numbers. But you can explore the specifics of your database on your own. If you are interested in reading more about one of the more common index types (b-tree), this entry by Matt Fleming is a pretty good read.

...Read More

Small tip: Pre-selecting a series of checkbox options

A question that comes up pretty frequently on forums is how to pre-select a set of form fields on some type of edit page. A typical example being something like this:

"I have an entry form that allows users to select multiple options from a series of checkboxes. When the form is submitted, the user's selections are saved to a database table. Now on my edit page I need to display the same set of checkboxes, but I would like to mark the user's previous selections as checked."




...Read More

Tuesday, July 21, 2009

SOT: Why JVM's Have No Class (.. or do they?)

So last week I was working with an external jar from ColdFusion and ran into what I thought was your typical ClassNotFoundException. It was perplexing because I knew the jar was accessible to the Classloader. So I could not figure out why the jvm was saying the class could not be found. At the time I was mainly focused on fixing the problem, but something about the exception struck me as odd. Rather than the typical ClassNotFoundException, the error message showed a chained exception that looked something like this:

java.lang.ExceptionInInitializerError
...
...
Caused by: java.lang.NoClassDefFoundError
...
...


...Read More

Monday, July 20, 2009

Displaying a PDF within CFDIV

I saw a curious question on the adobe forums about displaying a pdf inside a cfdiv tag. When you do this, the pdf is not rendered and instead you just see the bytes of file. I believe this happens because a cfdiv is not a separate window. So you are essentially trying to change the headers mid-stream, which does not work. (I remember there was a good thread on this topic somewhere in the old adobe forums. I am not sure if it is still around.)

Now I do not know if there is a better method, but what does seem to work is embedding the pdf with either <object> or <embed>, depending on your needs. Though not highly tested, it worked for me with Safari, Internet Explorer 8 and FF 3.5. (Naturally this assumes the user's browser is configured to actually display pdf's ;).


<!--- MAIN PAGE --->
<h1>PDF within CFDiv</h1>
<cfdiv bind="url:viewer.cfm" />

<!--- VIEWER PAGE --->
<object type="application/pdf" data="myFile1.pdf" width="400" height="300">

Experiment with building your own CFDirectory List in Java - Part 2

Commons Way
When I first explored this idea, I considered using the apache.commons.io package. It has a lot of interesting classes like DirectoryWalker. DirectoryWalker is a nifty class that handles all the tedious work of traversing directories. So you can focus on what you want to do with the results, such as storing the matching file names in a List.

The filefilter package also has a nice set of time-saving classes that handle many common filtering needs. For example AgeFileFilter allows you to search files by date and the aptly named SizeFileFilter allows searching by file size.

You can also chain multiple filters together. The FileFiltersUtils class provides an easy way to combine multiple filters into one. Using the andFileFilter and orFileFilter methods you can create a new filter that will find files that match all of the input filters or any one of the filters. If you need to combine more than two filters, you can also use the AndFileFilter and OrFileFilter classes directly. Both classes have a method that accepts a List of filters.


FileFiltersUtil Example
<cfscript>
Utils = createObject("java", "org.apache.commons.io.filefilter.FileFilterUtils");
// Find files modified on or after July 1st
earliestDate = parseDateTime("2009-07-01");
laterThan = Utils.ageFileFilter( earliestDate.getTime() );
// Find files that are at least 10,000 bytes in size
minimumSize = Utils.sizeFileFilter(10000);

// Create a filter that finds files matching BOTH conditions
findBoth = Utils.andFileFilter(laterThan, minimumSize);
// Create a filter that finds files matching EITHER condition
findEither = Utils.orFileFilter(laterThan, minimumSize);
//...
</cfscript>


My Way
Ultimately, I decided to roll-my-own filter, borrowing a bit of the logic from the

...Read More

Sunday, July 19, 2009

Experiment with building your own CFDirectory List in Java - Part 1

While cfdirectory is a good tag, sometimes you just need just a bit more functionality in the filtering department. A recent question on stackoverflow asked whether it was possible to extend cfdirectory's filtering capabilities to perform an exclusive search (ie find all files that do not end with .cfm). The general consensus was no, and that the simplest work-around is to wrap the cfdirectory call in a function and use a QoQ to get the desired results. However, a poster named Nick piqued my curiosity with his comment about using java file filters. While he agreed it was not the simplest option, it got me to thinking: if you were to try and build your own cfdirectory list function in java, how might you do it?


<cfscript>
dir = createObject("java", "java.io.File").init("c:/myFiles");
contents = dir.listFiles();
// Display the path of each item within the directory
if (IsDefined("contents"))
{
for (i = 1; i < arrayLen(contents); i++)
{
WriteOutput(contents[i].getPath() &"<br />");
}
}
</cfscript>


Now let us say you wanted to list only the .doc files within a directory. This is where file filters enter the picture. You could create your own custom file filter that determined if a file matched a particular file extension. Then pass that filter into one of the listFiles() methods. The listFiles() method would then use the filter to restrict the results, returning only .doc files in the array.

How do you create a file filter?
There are different types of filters, but in this case you would probably use a FilenameFilter. The FilenameFilter class is actually a very simple interface. To design your own filter, simply create a new class that implements java.io.FilenameFilter. Then implement the mandatory accept(File dir, String name) method. Inside that method is where you would place the logic that checks file extensions. The only requirement of the accept() method is that it return true if a given file matches your criteria. That is about all there is to it. As you can see in the code below, creating your own filter is not that complicated.

Custom Filter

import java.io.File;
import java.io.File;
import java.io.FilenameFilter;

public class FileExtensionFilter implements FilenameFilter{
private String fileExt;
public FileExtensionFilter(String fileExt) {
this.fileExt = fileExt.toLowerCase();
}

public boolean accept(File dir, String name) {
if (name.toLowerCase().endsWith(this.fileExt)) {
return true;
}
return false;
}
}


Using the Filter

Note: The custom class FileExtensionFilter must be added to the CF classpath first. For example, place the compiled class file in C:\ColdFusion8\wwwroot\WEB-INF\classes\

<cfscript>
// Retrieve only .DOC files
dir = createObject("java", "java.io.File").init("c:/myFiles");
// Create the filter
filter = createObject("java", "FileExtensionFilter").init(".doc");
contents = dir.listFiles();
if (IsDefined("contents"))
{
for (i = 1; i < arrayLen(contents); i++)
{
WriteOutput(contents[i].getPath() &"<br/>");
}
}
</cfscript>


Now the only other major piece needed to truly mimic cfdirectory is a bit of recursion. So you can search subdirectories. The basic algorithm in java is the same as it would be in ColdFusion. Just loop through the contents of a directory and check each item. If the item is a directory, repeat the process. Since the algorithm is so similar, I will not go into it here.

Continued in Part 2

...Read More

Friday, July 17, 2009

QoQ: Invalid Column Name Hack (Quick Follow-up)

Just a small note. I noticed the CF9 beta documentation mentions a change in the IsArray function:

Changed behavior: if the value parameter contains a reference to a query result column, this function now returns True. For example: isArray(MyQuery['Column1']) returns True. (In earlier releases, it returns False.)

So the QueryAddColumn hack may be legal in CF9 after all.

Thursday, July 16, 2009

CF9 Beta: CFDOCUMENT + OpenOffice Can Convert Any Format to PDF? (Documents,RTF's and Excel Sheets .. oo.Oh!)

Okay, that is definitely an exaggeration. But I have been testing <cfdocument format="pdf" ..> with an OpenOffice 3 installation and was quite pleased to discover it seems like it can convert any format to pdf. Well, any format OpenOffice can convert.

The current version of the CF9 documentation is probably not as clear on this topic as it could be. So I did some poking around. In my initial tests I was able to use CFDocument to convert most Office formats to pdf, just by changing the srcfilevalue. Though I have not tested all of the 2007 formats yet, here are my results so far:

CFDOCUMENT Format="PDF"

Format | Succeeds Yes/No | (Notes)
--------------------------------------
XLS | OpenOffice=Yes | POI=No
XLSX | OpenOffice=Yes | POI=No
DOC | OpenOffice=Yes | POI=No (As documented)
DOCX | OpenOffice=Yes | POI=No (As documented)
PPT | OpenOffice=Yes | POI=Yes
RTF | OpenOffice=Yes | POI=No (This is implied by documentation for .doc)
VSD | OpenOffice=Yes | POI=Semi (No exception, but generated pdf is gibberish)

Overall, the binary formats (.doc, .xls, ...) seemed to be handled a bit better. There were a few formatting issues with some of the more exotic documents I tested, as mentioned here. But since I received the same results using OpenOffice directly, it suggests the issue is with OpenOffice.

My assumption would be that conversion requests from CFDocument are just passed from CF to OpenOffice. So if OpenOffice knows how to do the conversion, the request works. I will probably submit a bug/enhancement request in the public bug database to ask if they can clarify the full range of supported formats in the documentation. There are also a few open and known issues. So if you encounter any issues with the OpenOffice integration, make sure you check the release notes and bug database first.

OT: Scope Creep

Scope Creep - 1 result



Scope Creep [skohp] [kreep]
Usage:
You are being a real creep about this. Building a space shuttle is not in the scope of a web application project. End of discussion. No wait... will you be traveling on it?

Why Does ArrayAppend Return True and ListAppend Returns the List

Nothing major, but if you are the curious type there was an interesting question today on stackoverflow. It is one of those simple things that you probably use every day, but may not stop to wonder "why is it this way?".

Why does arrayAppend return true and listAppend return the list?

I suspect the mutability issue is probably the biggest reason. But I seem to recall another suggestion (maybe on houseoffusion.com). I believe the poster suggested it might be linked to the fact that arrays are java.util.Vector's. So it is very likely arrays use the Vector.add(Object) method. According to the api the add() method should return a boolean value:

http://java.sun.com/javase/6/docs/api/java/util/Vector.html#add(E)
http://java.sun.com/javase/6/docs/api/java/util/Collection.html#add(E)

But that does raise another question. Are there any circumstances under which ArrayAppend(..) would actually return false?

Tuesday, July 14, 2009

Everything is right . Well, except for the fact that it is all wrong (Class Loaders)

Some time back I wrote a CF translation of Paulo Soares' example of How To Sign a PDF with iText. As the java code required some of the newer iText classes, I used the JavaLoader.cfc to access a more recent version of iText. At one stage I ran into a MethodNoFoundException I just could not figure out. It turns out the primary cause was an error in my code and though I eventually figured out the surface cause, the deeper reason for the exception eluded me. But that question always stuck in the back of my mind. While reading about class loaders last week, the real explanation for the error finally hit me.

<cfscript>
// get a reference to my javaloader
javaLoader = server[application.MyUniqueKeyForJavaLoader];

// note: the document is created with createObject() and not the javaLoader
document = createObject("java", "com.lowagie.text.Document").init();
document.open();
paragraph = javaLoader.create("com.lowagie.text.Paragraph").init("Hello World");

// This does NOT work
document.add(paragraph);
</cfscript>


The Clue
On the surface it seemed like the code should work. But obviously it did not. I double checked everything from spelling to paths to cfdump'ing the objects. I went so far as to use javap to verify the method signatures. I even tried loading the built-in version of the iText.jar instead. As far as I could tell, everything was correct. Yet still the MethodNotFoundException persisted.

Eventually I figured out the difference was the usage of createObject. It seemed innocuous enough, but it turns out it had a greater effect than I thought. Apparently the children of createObject and javaLoader.create() are like the Hatfields and the McCoys: they do not get along. (At least not with the default settings, which is what I was using). I knew the breakdown in communication had something to do with the fact that the objects were created with two different class loaders: one by the JavaLoader and the other by ColdFusion's class loader. But I was not sure why that made a difference.

The Answer
The light finally dawned while reading a great article in the O'Reilly series. The article provides a detailed description of exactly how class files are loaded. One of the key steps in the process is the findClass() method. Typically, the findClass() method is where the class loader grabs the actual byte codes for a given class. After the bytes are loaded, another method called defineClass() is invoked. That method is responsible for converting the bytes "into an instance of [the given class]". In other words, it creates the new instance for you when you call init() on a java object. Now here is the comment that set off a light bulb in my brain:

... the runtime is very particular about which ClassLoader instance calls [defineClass] ...if two ClassLoader instances define byte codes from the same or different sources, the defined classes are different.
And the sign said "Long-haired freaky people, need not apply"
So my two objects were incompatible because the JavaLoader and the ColdFusion class loader had each generated their own definitions of the class. According to the rules the two definitions are not considered equal. So when I attempted to pass the one object into the other:

<cfscript>
// This does NOT work
document.add(paragraph);
</cfscript>
.. CF spewed out an error message. Not because the document.add(...) method did not exist, but because the existing method would not accept what I was passing into it. I may as well have tried to pass in a brass trombone. From the class loader's perspective, there would be little difference. It would not recognize either one, so both would be rejected equally. (Interestingly, these same rules apply even if you were to use two separate instances of the same ClassLoader class).

So I finally answered that nagging question and temporarily silenced the curious child in me that is always asking "But why?". If you are interested in learning more about class loaders, I would recommend the Internals of Java Class Loading article. Though a bit dated, it provides an excellent overview of the topic.

...Read More

CF9 Beta: Convert .DOC files to PDF (..if you have OpenOffice)

While I knew about the spreadsheet integration in CF9, I was very surprised to discover handling for other Office formats. Specifically the new cfdocument feature that can convert .doc files to .pdf format. Now for the catch: it requires an installation of OpenOffice. But if you can live with that caveat, it is a pretty sweet feature.


<cfdocument format="pdf"
srcfile="c:\myFiles\testWord2003.doc"
filename="c:\myFiles\testWord2003_Converted.pdf" />

So far the results for the .doc files I have tested have been very good. Cfdocument did a fairly good job converting images, links, and even creates internal page links when converting a table of contents. Though not surprisingly it did nothing with embedded objects (like an embedded .zip file). The resulting pdf contained only the icon of .zip file, not the binary data itself. Of course there are still a bunch of new attributes to look at, like formfields, formsType and of course the new in memory files feature.

One interesting thing I noticed in the current documentation is that it says:
... the cfdocumenttag lets you read, write, and process Word documents and PowerPoint presentations. All versions of Microsoft office applications from 97 to 2003 are supported.

While it does not mention anything about 2007, OpenOffice is capable of converting .docx files. So I decided to test the theory and feed in a .docx file. Sure enough, it successfully converted the file to .pdf format. Though after testing several files, I began to suspect the omission of 2007 may have been deliberate.

OpenOffice can do a fairly good job of converting .docx files, but it occasionally choked on some of the more elaborate ones. Curiously, one of the files it garbled was one I used when testing the OpenXMLViewer program. I am not sure if it was the vml in the source document, or something else. But with that particular document, OpenXMLViewer and docx4j did a much better job at the conversion than OpenOffice. So perhaps 2007 was not listed as "supported" because there were a larger number of quirks with that format. Still, it would seem to be an option. Though probably an unsupported one ;)

The Man Who Fell to Earth
When I first started testing word converters a few months ago, I think I had some naive idea that there was a magical program out there that could instantaneously transform any file into any format. Obviously there is no such thing. Not in the commercial or open source realm. So if you are envisioning a world of perfect peace, where all file formats exist in complete harmony, one hundred percent of the time .. go back to bed. You are still dreaming. But if you take a more level-headed approach, and keep in mind some of the practical limitations, you may find that the available tools can get you pretty darn close.

Note, my comments above are not intended to disparage the new cfdocument features, or any of the tools I have tested. But the fact of the matter is

...Read More

Monday, July 13, 2009

CF9 Beta: Small POI and OOXML Tip

I noticed the CF9 beta uses POI version 3.5-beta5-20090506. I am assuming it is what powers the cool new <cfspreadsheet> tag.

Now I do not know if POI is used for anything else, but the nice thing about version 3.5 is that it includes ooxml handling. That means you can use neat tricks like these, right out out of the box. No extra jars or javaLoader needed.

CF9 Beta: What version of iText does it use?

Since I love everything iText, one of the first things I did after installing the beta was check the iText version. (For the lazy, it is iText 2.1.0).

How to determine the version of iText

CF9 Beta: Finally, CFFinally

Though not a major feature, I am very pleased to see the cffinally tag in the ColdFusion 9 Beta. I have always missed the full try/catch/finally construct found in other languages.

While you could manually structure your code to simulate it, the absence of a finally clause makes it easier to overlook important cleanup tasks. For example, when working with lower level java objects it is easy to forget to close file objects or database connections. That can lead to problems with locked files or too many open connections. So I think the additional of finally will help promote better coding habits.


<h2>IText - HelloWorld Example with CFScript / Finally</h2>
<cfscript>
outputFile = ExpandPath("./HelloWorld.pdf");
document = createObject("java", "com.lowagie.text.Document").init();
outStream = createObject("java", "java.io.FileOutputStream").init(outputFile);

try {
writer = createObject("java", "com.lowagie.text.pdf.PdfWriter").getInstance(document, outStream);
document.open();
paragraph = createObject("java", "com.lowagie.text.Paragraph").init("Hello World");
document.add(paragraph);
// Deliberately cause an error
z = 10 / 0;
}
catch (java.lang.Exception e) {
WriteOutput("Entering Catch Clause! Message:"& e.message &"<br>");
}
finally {
WriteOutput("Entering Finally Clause!" &"<br>");
if (structKeyExists(VARIABLES, "document") && document.isOpen()) {
document.close();
}
if (structKeyExists(VARIABLES, "outStream")) {
outStream.close();
}
}
</cfscript>

CFML Example:

<h2>IText - HelloWorld Example with CFFINALLY</h2>
<cftry>

<cfset outputFile = ExpandPath("./HelloWorld.pdf") />
<cfset document = createObject("java", "com.lowagie.text.Document").init() />
<cfset outStream = createObject("java", "java.io.FileOutputStream").init(outputFile) />
<cfset writer = createObject("java", "com.lowagie.text.pdf.PdfWriter").getInstance(document, outStream) />
<cfset document.open() />
<cfset paragraph = createObject("java", "com.lowagie.text.Paragraph").init("Hello World") />
<cfset document.add(paragraph) />
<!--- Deliberately cause an error here --->
<cfset z = 10 / 0 />

<cfcatch type="any">
Entering Catch Clause! Message: <cfoutput>#cfcatch.message#</cfoutput><br/>
</cfcatch>

<cffinally>
Entering Finally Clause!<br/>
<cfif structKeyExists(VARIABLES, "document") AND document.isOpen()>
<cfset document.close() />
</cfif>
<cfif structKeyExists(VARIABLES, "outStream")>
<cfset outStream.close() />
</cfif>
</cffinally>
</cftry>

...Read More

Sunday, July 12, 2009

Good Things Come To Those Who .... Stay Up Late (ColdFusion 9 Anyone?)

I was pleasantly surprised to see these announcements a short while ago:

ColdFusion 9 and ColdFusion Builder in the wild

Adobe releases public beta of ColdFusion 9 and ColdFusion Builder

Friday, July 10, 2009

Java: Eclipse Tip / Change a "General" to a "Java" project

A few weeks ago I had problems importing a java project into Eclipse. I ended up with a "General" project, instead of a "Java" project, which meant the build path option was disabled. With the help of this useful tip it was fixed in seconds.

Eclipse: Changing a General project to a Java project

Wednesday, July 8, 2009

SOT: Why do people forget about INSERT/SELECT statements (I-Scream)?

A recent response by Peter Boughton on stackoverflow.com pointed out a commonly overlooked technique for handling multiple selections from a set of database values. Well it reminded me that I wanted to write a small entry on the bizarrely underused INSERT/SELECT method. (Now chances are if you are already familiar with it, you are probably not reading this entry ;) So in summary, a SELECT statement can be used to INSERT multiple records into another table. Frankly, it is so simple I do not know why it is not used more often.

<cfquery name="getFlavours" datasource="#MyDSN#">
SELECT FlavourID, FlavourName
FROM IceCreamFlavour
ORDER BY FlavourName
</cfquery>

<form method="post" action="saveFavorites.cfm">
<b>Select your favorite ice cream flavours:</b><br/><br/>
<cfoutput query="getFlavours">
<input type="checkbox" name="flavourID" value="#FlavourID#">#FlavourName#<br/>
</cfoutput>
<br/>
<input type="submit" name="saveMe" value="Save My Flavours!" />
</form>



Form



Now when it comes time to inserting the selected values into a database, you will often see code using a <cfloop> to INSERT the values one-by-one. While that does work, it is not the most efficient technique because it requires multiple queries. That creates another disadvantage. With multiple queries, there is always the possibility of leaving the database in an inconsistent state if an error occurs. Of course that can be solved with <cftransaction>, but there is a simpler option.

Since the source of the values is a database table, you can use a SELECT statement combined with an IN (...) clause to retrieve the id's selected and INSERT them into another table. All within the same query.

Validation omitted for brevity

<cfquery name="saveData" datasource="#MyDSN#" result="results">
INSERT INTO FavoriteFlavour ( UserID, FlavourID )
SELECT <cfqueryparam value="#session.userID#" cfsqltype="cf_sql_integer">,
FlavourID
FROM IceCreamFlavour
WHERE FlavourID IN
(
<cfqueryparam value="#form.flavourID#" list cfsqltype="cf_sql_integer">
)
</cfquery>

As a side benefit, you also get some extra validation. Since a SELECT is used to retrieve the values, only valid record id's will be inserted into the second table. While not the solution for every INSERT, you might be surprised at how flexible and useful this type of INSERT can be.

...Read More

Monday, July 6, 2009

Four and Twenty Blackbirds Baked in a Pie Chart

I read a question last week about creating a flash cfchart pie chart with a transparent background. For whatever reason I was convinced this was a simple task, and could have sworn I had done it before. Well it seems I was wrong and must now eat crow.

After checking the trusty webchart3d utility I did find a transparent setting for background color. But it appears to do nothing for png charts. They are always saved as opaque. Eventually, I did find a hack for flash charts (only). But it does require tweaking the output a bit.

<?xml version="1.0" encoding="UTF-8"?>
<pieChart depth="Double" style="Solid" angle="340" is3D="false">
...
<insets left="5" top="5" right="5" bottom="5"/>
<background type="Transparent"/>
</pieChart>

Next, use the custom style to generate the cfchart. But capture the output with cfsavecontent so you can manipulate it.

<cfsavecontent variable="chartOutput" >
<cfchart format="flash" style="transparent_Pie.xml">
<cfchartseries type="pie">
<cfchartdata item="Yes" value="750">
<cfchartdata item="No" value="550">
</cfchartseries>
</cfchart>
</cfsavecontent>

Finally, use a few string functions to insert the "wmode" setting to make the background of the swf transparent.

<!--- add "wmode" parameter to OBJECT tag --->
<cfset newParam = '<param name="wmode" value="transparent">'>
<cfset newOutput = reReplaceNoCase(chartOutput, "(<OBJECT [^>]+>)", "\1"& newParam, "all")>
<!--- add "wmode" attribute to EMBED tag --->
<cfset newParam = 'wmode="transparent"'>
<cfset newOutput = reReplaceNoCase(newOutput, "(<EMBED [^>]+)", "\1 "& newParam &" ", "all")>

Now the chart's background will be transparent. Definitely a hack. But if anyone knows of a cleaner work-around, I would love to hear it.

<!--- display the chart --->
<div style="background-color: #ff0000;">
<cfoutput>
#newOutput#
</cfoutput>
</div>

...Read More

Saturday, July 4, 2009

QoQ: Invalid Column Name Hack

I saw a question the other day about how to rename query columns with invalid names. Now in the poster's case, renaming really was not needed. Like most query ills it was solved with a simple bit of array notation: #queryName["column-name"][rowNumber]#

But in the process I did stumble upon another way to rename a column. In a pinch, you can use QueryAddColumn() to make a copy of it under a new name. Just pass in a reference to the query column in place of the array of values. Though this verges on the undocumented, it does work with both MX7 and CF8:


<cfset queryAddColumn(myQuery, "BetterColumnName", myQuery["Awful-Column-Name"]) />


Why does it work?
When you add a column with the QueryAddColumn() function, the values are passed in as an array. If you take a look at that function internally, you will see that the argument type is actually a java.util.List

<cfset funcObject = createObject("java", "coldfusion.runtime.QueryFunction")>
<cfdump var="#funcObject#" label="Query Functions">



Since ColdFusion arrays are java.util.List objects internally, that makes sense. But that also means that you can pass in any java.util.List object.

Now if you create a query, and examine one of its columns, you will discover the query column is also a java.util.List object. That means the QueryAddColumn() function will accept it the same way it would a ColdFusion array object.


<cfset myQuery = QueryNew("SomeColumn")/>

<cfoutput>
<b>Query Column Class:</b> #myQuery.SomeColumn.getClass().name#<br/>
<cfset interfaces = myQuery.SomeColumn.getClass().getInterfaces() />
<!--- Show the interfaces this class impements --->
<cfloop array="#interfaces#" index="inter">
Implements: <cfdump var="#inter.getName()#" label="Interface"/><br/>
</cfloop>
</cfoutput>

So there you have it. Again, this is not documented behavior. But I thought it was an interesting find nonetheless.

Related Entry: QoQ: Invalid column Name Hack (Follow-up)

...Read More

Friday, July 3, 2009

OT: Null Characters Bring out the Beast

Once every blue moon, I am reminded of the fact that you cannot create a null character in ColdFusion. At least not in any of the ways you would expect.

When the moon rises, I quickly run through the logical possibilities chr(0), javacast("char", chr(0)), etcetera. Only to find they do not work. Each new attempt producing everything from a space character to an empty string. But no null character in sight.

As my frustration grows, my nails begin to lengthen. My flesh erupts in fur and my teeth transform into fangs. My nails gouge the keyboard as I begin to howl. Then I remember. There is a way. Somewhere in the tombs of google there is an ancient article on Null Characters in ColdFusion.

Searching google as quickly as my claws will allow, I find it. My fangs begin to recede. I try it. My fur transforms back into flesh. It works. I am safe for another few years.

Good thing too, because trying type with claws is pretty rough on the average keyboard. Well, I am off to the computer store to buy a new one. Maybe I will pick up two .. just in case.

(Yes, it has been that kind of a day. Grrrr)

Wednesday, July 1, 2009

ColdFusion: Experiment Converting MS Word to HTML/PDF - Part 2 Example

A brief addendum to Part 2. Here is an example of using a OpenXMLViewer.exe from a .bat file. Just update the directory to match the location of the OpenXMLViewer folder on your system.

Note, the cd (change directory) statement in the first line is important. If you have not added the OpenXMLViewer folder to your PATH environment variable, you must call the program from within the parent directory so it can locate the needed dependency files.

BAT File


@ECHO OFF
REM **********************************
REM %1 Full path to the source file *.docx
REM %2 Full path to output folder (ie directory not file name)
REM %3 Browser type (IE, FIREFOX or OPERA)
REM **********************************

cd % C:\tools\OpenXMLViewer\
OpenXMLViewer.exe %1 %2 %3

...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep