| By Ben Forta | Article Rating: |
|
| August 23, 2002 12:00 AM EDT | Reads: |
14,758 |
Discovering undocumented features in your favorite application is always something of a thrill. This is especially true when those features expose little nuggets of functionality that you can leverage in your own code. And so, as promised in my last column, I'm going to introduce you to the factory, a set of internal ColdFusion MX services exposed via Java objects and APIs.
The Factory
When most of us think of ColdFusion we instinctively think of CFML, the ColdFusion Markup Language. This is understandable: after all, it's CFML that we spend most of our time with.
But ColdFusion is actually much more. It's also a collection of application services and runtime services, and has to be. When you build a dynamic SQL query statement something must process that and build the query. When you execute queries something needs to track that information in order to display debug output. When you use <CFCHART> an entire charting engine must be present to execute your request. The list goes on and on.
ColdFusion 5 (and earlier) never exposed these core services. They were built into ColdFusion and were for the most part inaccessible. ColdFusion MX changes this quite a bit. Built on underlying Java code and Java openness, CFMX includes all sorts of services that perform all sorts of processing (starting with the examples I just mentioned).
For example, consider debugging. Ever since ColdFusion 1.0 days, debug output has been appended to the bottom of generated pages. CFMX still generates the same debug output, but it also offers a new DHTML-based dockable debug screen (which is much easier to use). If you've ever dug around in the ColdFusion directory structure, you'd have found a directory named debug (under CFMX's WEB-INF directory). In it are three CFM files:
- classic.cfm: ColdFusion code that emulates classic ColdFusion debug output, appended to the bottom of each page
- dockable.cfm: ColdFusion code that generates the new DHTML-based debugging
- dreamweaver.cfm: Used by Dreamweaver MX to obtain debug content for displaying in Dreamweaver's own Debug tab
In other words, unlike CF5 and earlier, CFMX doesn't automatically append debug information to pages. Rather, it collects that information and exposes it via APIs so that regular CFML code can be used to render it as needed. The debugging appended to the bottom of your generated code is placed there by CFML code that emulates the behavior of earlier versions of ColdFusion. You could create your own debug output formats (modeled on the ones in the debug directory) and then save them in this directory so that the ColdFusion Administrator could see them and make them available as debugging formats.
And talking about the ColdFusion Administrator, it too is good old CFML. How does it get to validate datasources, set SMTP servers, and more? Answer: it uses APIs exposed by ColdFusion and makes calls to them.
Which brings us back to the factory and integrated services. The factory is a set of objects that expose all sorts of integrated services. It's used by the just-mentioned debug pages, as well as by the ColdFusion Administrator. You can use it too.
A Visit to the Factory
Intrigued? You should be. This starts to be lots of fun very quickly. The factory is a set of Java objects, so interaction with it is via the <CF OBJECT> tag (or the CreateObject() function if using <CFSCRIPT>). Look at this code:
<!--- Access factory --->
<CFOBJECT ACTION="CREATE"
TYPE="JAVA"
CLASS="coldfusion.
server.ServiceFactory"
NAME="factory">
This <CFOBJECT> creates a local object (a ColdFusion object) named factory that can then be used to access factory services. You can execute the foregoing line of code as is. It will work (in CFMX, not in earlier versions), but won't actually do anything with the instantiated object. To see what's inside the factory, you can simply <CFDUMP> the object like this:
<!--- Access factory --->
<CFOBJECT ACTION="CREATE"
TYPE="JAVA"
CLASS="coldfusion.
server.ServiceFactory"
NAME="factory">
<!--- Dump factory contents --->
<CFDUMP VAR="#factory#">
Execute this code and you'll see a dump of a complex nested structure containing objects, methods, and more. Everything you see on your screen is accessible to you (if you can work out what they do, what parameters they expect, and what assumptions they make - hey, that's what makes this fun!).
Accessing Factory Services
Now that you know how to access the factory, the next thing is to learn how to access specific services. Look at the top-level objects in the previous <CFDUMP>. You'll see names like DataSourceService and DebuggingService. To access any particular service just use the appropriate get method. For example, the getDataSourceService() method returns the DataSourceService, and getDebuggingService() returns the DebuggingService. To access the DebuggingService you could do the following:
<!--- Get "factory" --->
<CFOBJECT ACTION="CREATE"
TYPE="JAVA"
CLASS="coldfusion.
server.ServiceFactory"
NAME="factory">
<!--- Get datasource service --->
<CFSET dsService=factory.
getDataSourceService()>
dsService then provides access to the DataSourceService itself. You can <CF DUMP VAR="#disservice#"> to see what it contains (it'll be a subset of the prior <CFDUMP>).
Using the Datasource Service
For our first example we'll use the datasource service. Suppose you want to programmatically validate that a datasource was valid. There's no CFML function to do this, but it must be doable. After all, the ColdFusion Administrator does it! Well, it is indeed doable, using the factory (just as the ColdFusion Administrator does).
Listing 1 is an example I used last month when discussing UDFs, and as promised, the following is the explanation of how it works.
VerifyDSN() does just that: you pass it the name of a datasource and it'll return true if that datasource can be verified (as usable) or false if not. VerifyDSN() accepts one required parameter - the name of the datasource to be verified. Let's walk through the code together.
The function and required argument are defined using <CFFUNCTION> and <CFARGUMENT>. (These were explained in the August column [Vol. 4, issue 8]).
Next, two local variables are defined: dsService will contain the datasource service object, and result is the return value (which is initialized to true - kind of innocent until proved guilty).
Then a try/catch block is defined. One of the things you'll learn very quickly about the methods exposed by the factory is that they don't throw pretty error messages or let you know when you messed up passing an argument. You need to use them exactly as intended or they will throw error messages. More on this in a moment.
Next comes the factory access code I showed you earlier.
The actual DSN validation is performed by the verifyDatasource() method, which takes the name of a datasource as a parameter and returns true if it can be validated and false if not. Whatever verifyDatasource() returns is saved to the result variable.
verifyDatasource() requires that a valid datasource name be provided; if the name doesn't exist, an error will be thrown. There are methods that return a list of datasources, so the function could first have done that and checked the name to see if it was valid, but why bother? We already trap errors with try/catch handling. If verify Datasource() throws any error at all (including an error because the specified datasource didn't exist), we'll just treat it as a false, which is exactly what the <CFCATCH> block does.
And finally, <CFRETURN> returns result, which will be true if the datasource can be validated, or false if not (either because the validation failed or because an error was thrown).
VerifyDSN() can now be used just like any other function:
<CFOUTPUT>
#VerifyDSN("exampleapps")#
</CFOUTPUT>
If you do indeed want to obtain a list of datasources, the getDatasources() method can help. Listing 2 is an example.
This time getDatasources() is used to obtain a full datasource list. This method returns a complex structure, the top level of which contains the datasource names, and beneath are complete datasource details. As all we want are the names, the StructKeyToArray() function is used to return an array containing the top-level key names - in other words, an array of datasource names. The names are then dumped in a simple HTML unordered list.
Using the Debugging Service
Of particular interest is the debugging service, the service used by the debugging engine in ColdFusion. This is what drives the debug templates referred to previously.
The debugging service doesn't generate any output. Rather, it keeps all information that may be of use in debugging in a query in memory. The code in the debug templates extracts data from that query and presents it as needed. And if that code can access the query, well, so can you.
A function I've always wanted was one that could return the exact SQL statement as submitted to a datasource post any dynamic processing. ColdFusion obviously knows this information - after all, it's in the debug output and always has been, but there's been no way to access it. Execution time, record count, column list - those are all accessible, but not the SQL itself.
Well, if the debugger knows this information, and in CFMX your code can know everything that the debugger knows...see where I'm going? Take a look at the code in Listing 3.
QueryGetSQL() takes the name of a previously executed query and returns its SQL statement. Let's walk through the code.
The function and argument are defined and variables are then initialized.
The debugging service collects information only if debugging is needed (which actually makes perfect sense). This means that this function can be used only if debugging is enabled, so the processing is enclosed within a <CFIF IsDebugMode()> condition.
The debugging service is then accessed just as we did earlier with the datasource service. The debugging information itself is stored in a query, as already explained, and the getData() method exposes that query. Thus the following snippet creates a local query named events that contains the debug information:
<!--- Get the event table --->
<CFSET events= cfd.getDebugger().getData()>
The events query contains all sorts of information (do a <CFDUMP> - you may find something intriguing or useful). So how to extract the single column we need for the one desired entry? We can query that query:
<!--- Get SQL statement (body) for query --->
<CFQUERY DBTYPE="query"
NAME="getquery"
DEBUG="false">
SELECT body
FROM events
WHERE type='SqlQuery'
AND name='#queryname#'
</CFQUERY>
Each entry in the events query has a type. The type we want is SqlQuery (other types contain other debug information). The WHERE clause filters this type for the specified query name - this way we obtain just the row we want. Rows are made up of lots of columns, including one named body which (when type is SqlQuery) contains the SQL statement. That's the one column we want.
The function then saves that body column to result and returns the value. Simple as that.
Using the Runtime Service
One of the most intriguing (and, when you look at the method names, scariest) services is the runtime service. I'm still working my way through this one, but here's one method that's already finding its way into my applications.
Ever wondered how you could easily determine if the SESSION scope was safe to use (i.e., session state management was enabled)? The runtime service contains a method named getVariables() that returns a structure containing two other structures, application and session. Each of these structures contains (among other things) a member named enable, which will be true if enabled and false if not. I'm not going to give you the code to access these - try to work it out yourself.
Ah, the possibilities are mind-boggling!
Summary
ColdFusion MX is built on a brand-new, completely redesigned architecture - you already know that. But what you may not have known is just how open and flexible this architecture is. The factory is a perfect example of this openness, and while its use is undocumented, it's also perfectly legal. Want to create an administrative program of your own? It's doable. Want access to internal data? That's doable too. Want to create graphs, work with log files, tweak the schedule, or read and write CF Admin settings? All that is doable. Want to create your own debug screens? As already explained, even that is doable.
When you tinker at this level, of course, you're on your own (never experiment on production servers!). There's no documentation on this stuff. It's undocumented - anything I've shared here was learned by browsing files and experimenting - but that's just part of the fun. So give it a try, and if you create anything really cool I'd love to see it (or submit it to www.cfml.org if you can turn it into a function). Enjoy!
SIDEBAR
Caveat emptor:
Undocumented means unsupported - there are no examples, no documentation, and no support. In addition, any undocumented feature may be changed or removed in future versions of products. That's what distinguishes features from undocumented features; vendors are under no obligation to support anything undocumented. Use at your own risk.
Published August 23, 2002 Reads 14,758
Copyright © 2002 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Ben Forta
Ben Forta is Adobe's Senior Technical Evangelist. In that capacity he spends a considerable amount of time talking and writing about Adobe products (with an emphasis on ColdFusion and Flex), and providing feedback to help shape the future direction of the products. By the way, if you are not yet a ColdFusion user, you should be. It is an incredible product, and is truly deserving of all the praise it has been receiving. In a prior life he was a ColdFusion customer (he wrote one of the first large high visibility web sites using the product) and was so impressed he ended up working for the company that created it (Allaire). Ben is also the author of books on ColdFusion, SQL, Windows 2000, JSP, WAP, Regular Expressions, and more. Before joining Adobe (well, Allaire actually, and then Macromedia and Allaire merged, and then Adobe bought Macromedia) he helped found a company called Car.com which provides automotive services (buy a car, sell a car, etc) over the Web. Car.com (including Stoneage) is one of the largest automotive web sites out there, was written entirely in ColdFusion, and is now owned by Auto-By-Tel.
- Oracle To Keynote Cloud Computing Expo
- Contrary Opinion: Why Silverlight is Good for Adobe
- Analytics for Adobe Air Applications
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Eval JavaScript in a Global Context
- Fig Leaf Software to Exhibit at Government IT Conference & Expo
- Is Microsoft as Free as Open Source?
- Cloud Computing Journal: Adobe to Deliver ColdFusion in the Cloud
- The Planet Named “Bronze Sponsor” of Cloud Computing Expo
- Adobe Reader Sued
- AJAX World RIA Conference & Expo Kicks Off in New York City
- Adobe Enters Cloud Computing with LiveCycle
- Oracle To Keynote Cloud Computing Expo
- Social Media Terrorists
- Adobe Flash Media Server on iPhone
- Contrary Opinion: Why Silverlight is Good for Adobe
- Adobe Flash Based GetJar Surpasses a Half Billion Downloads
- Adobe ColdFusion 9 and ColdFusion Builder Public Betas Now Available
- Adobe Tries Commercializing Its Online Software
- Adobe Open Sources Flash Initiatives
- The Next Programming Models, RIAs and Composite Applications
- Where Are RIA Technologies Headed in 2008?
- Constructing an Application with Flash Forms from the Ground Up
- AJAX World RIA Conference & Expo Kicks Off in New York City
- CFEclipse: The Developer's IDE, Eclipse For ColdFusion
- Personal Branding Checklist
- Adobe Flex 2: Advanced DataGrid
- Has the Technology Bounceback Begun?
- Building a Zip Code Proximity Search with ColdFusion
- i-Technology Viewpoint: We Need Not More Frameworks, But Better Programmers


































