CF: Allowing different extensions for scheduled task log files

This is a quick post for people that run into this dilemma where there scheduled task stop working upon upgrade. They may receive this type of error in their browser:

Initialization Error: Valid extensions are : log,txt. –
Invalid extension of the file name.

At first you go “What the Heck!”. Then, look through gazillion lines of code to find where we could have produced this error and could not pin-point it.
Then, more digging to find the culprit:
With the advent of ColdFusion 10 & 11 and the exposure of the scheduled task vulnerability there was a change to what extensions where supported for your log files when you schedule tasks. As a security element these files can only be log and txt files.

However, the raw capture of the task run is normally neither, more often than not, especially with debug for the local IP enabled the output is HTML.
Thus, we have, now, for many years, used htm as the preferred extension, since the raw capture of the task run is HTML, thus, easy to view in the browser.  Forcing it into txt would only makes us rename the file before opening in the browser again.

Nightmares of unneeded code change ensued…

Fortunately, the solution seems simple enough.
a) Stop the ColdFusion instance
b) Find the neo-cron.xml
c) find the line that read like txt,log
d) add your extension to that line, e.g. txt,log,htm
e) start instance

Hope this helps others who give themselves the “Duh” slap.

Cheers,
B.

CF: CCFML or Making the Case for a Different CFML Future

Looking at the demands of our enterprise and the product road-maps as far as they have been disclosed by either Railo or Adobe we discovered a gap between what we are trying to achieve and what the technology is going to offer.

So, I have taken upon me to summarize a few thoughts and suggestions that I would like to share to see whether we can sway the Railo/Adobe general product direction.

I believe that focusing ever more innovation resources on the concept of RAD (Rapid Application Development) and language improvements, though interesting and useful, are not making CFML standout sufficiently to make a long-term difference and detract people from leaving or encouraging  new people to join.

In my opinion, the next level of server improvements need to be substantially different from other offerings and, this, in turn, requires a rethinking of what Railo/ACF offerings are.
Specifically,
Abandonment of the concept of Application Servers
Remove the need for download/installs
Remove the need for server administration and management
Redefine offering as packaged “infrastructure” with smart policy and deployment
Use clear convention based guidelines for subsystems (cache, application, storage, db)

Ok, now you say, what the heck are you dreaming about?
Good question, that.

In short, I am proposing that we work towards a true Cloud CFML platform === CCFML.

Let me explain:

We come from a heritage of dealing with individual servers and have embraced that concept wholeheartedly for many years. However, in the age of the cloud we should question those expectations.
How much more attractive could CCFML be when you only need a github/subversion account to deploy your code and a few policy definitions on how large you want your Application to scale and how fast.
When there are application problems, you can define policies how much CPU a process may consume, add more CPU cycles specifically for it, and/or cache. All automatically.
You application works automatically, fully scalable, across the globe on CFML.
You are kept abreast of any problems, bottlenecks and are given options to upscale CPU, refactor code, or add instances. But, best of all, you let the Cloud CFML take care of everything that a global CFML service should.
You want to push a new version, just change code and click deploy/schedule button.

Whatever decisions need to be made to get us this platform nirvana should be in the forefront of cfml future enhancements. This would require more standardization of convention so that we can have auto- configuration over coding whenever possible.
Behavior for deployment will need to be defined and “standard conventions” documented.
Here is just a selection of things that need to be answered on the way towards such a CCFML platform:
Which distributed cache to implement and deploy
Deployment system magic (version, install, upgrade, start, stop, pause engines)
How do you scale session across servers
How to communicate among cluster members
How do you join servers to clusters
How do you create a unified lightweight Application scope across the cluster,
How to measure and set time,
How to define and use a shared file-system
How do you delineate code files storage vs. user assets
How do you provide Application policy definitions for scaling
How to manage and analyze code performance
Create Application Manager (instead of Server Manager)
Assigned Server roles, e.g. stateless vs. statefull servers
Global logging and analysis
Etc., etc.
There are probably many elements I don’t quite understand or have not considered. This is where I would ask for the smart people of the community to jump in, but, the point I am trying to emphasize is that future innovation should be directed towards creating such a platform rather than focusing on the minutiae of the language syntax.

The CFML language is quite mature and tweaks on syntax can only have limited impact on stopping developer attrition while an easy to use application platform has the potential to attract developers.
This model also provides a clear path for providers of this tech to charge for the services with the value recognition they are looking for.

The good news is that we have many components already; the next step is to create the working package and provide CFML as a first class cloud service.

We would not be the first, others like Microsoft with Azure and .net and the Java/Scala Play! Frameworks have already started the thought model and are getting traction. I fear that if we hesitate too long we may miss an opportunity to reverse course.

I hope this is not too confusing of a ramble and I am looking forward to feedback.

-Bilal

CF: Railo: Using VisualVM tool to monitor running Railo servers

I had written a Adobe ColdFusion specific article on how to use the free VisualVM tool to get insight into the workings of the Java Virtual Machine. I have since been asked to provide similar guide for Railo CFML engine.

The good news is that the implementation is very similar and mostly follows the same path. I will demonstrate this using Windows OS example. I assume in this example that you have used the standard Railo installer for windows.

If so, here are some simple steps to use this great tool set working with Railo.

1) download java jdk also referred to as Java SE Development Kit. You can use 1.6.38 or later or 1.7.13 or later.  Again, Important to get the JDK not the JRE.
http://www.oracle.com/technetwork/java/javase/downloads/

2) Download Visual VM:
https://visualvm.dev.java.net/

3) Configure Railo jmx access:
On Windows, best way is to go to Tomcat Service Control, open the Java panel and add the following to the Java Options text box: (you can change the ports etc. this is is my sample):

-Dcom.sun.management.jmxremote.port=8701
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

You can decide whether to use ssl or not, and also on the port to use. If you want to use jmx authentication I would recommend you read:
http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html#gdenl

You will need to restart your server after you have completed your changes.

4) Configure your Visual vm start up to point to your jdk if you have not set environmental variables:
e.g. on Windows
if you extracted the visualvm files into C:\visualvm135
and
Your JDK is located in C:\Java\jdk1.7.0_13
then you can use the following command line:

C:\Java\visualvm_135\bin\visualvm.exe –jdkhome “C:\Java\jdk1.7.0_13”

You can also add a batch file shortcut for reuse, e.g.

5) Start up the VisualVM tool (it may have to go through calibration first, simply acknowledge), then, and establish a connection a JMX connection by right clicking on the local node and choosing “Add JMX Connection…”

6) Add connection parameters:
If the server is local you can use: localhost:[port] in our case: localhost:8701

Now you should be able to monitor basic statistics of your Railo environment as it runs, and do some nifty things like forcing garbage collection and dump heap files for later analysis.

Cheers,
B.

CF: cfObjective() 2013… talk is cheap (relatively ;o)

I decided to submit my slew of topics to this year’s cfObjective conference. I had many things that I wanted to investigate or thought were worth sharing… never can pick really…
Fortunately for me the selection committee picked two of my submissions and, thus, if you time it right and have no better place to be, come join me at cfObjective in Minneapolis to dig into the mobile development realm …

“Hey Bilal, come to the point”, you say. “What are you going to yak about, since I am biZy! With a capital Z”.
Well,  ok, here are the things I am going to talk about:

Design MVC Mobile App Visually In Hours

HTML5 / CSS3 / JavaScript Mobile Apps are becoming more popular. Unfortunately, the tools that we use to design them have not changed much. This talk is centered on using more advanced design tool such as Sencha Architect 2 to visually create front-end MVC mobile prototypes quickly in WYSIWYG fashion. We will step through the elements and create our own app and discuss native deployment options as well.
We will learn how to navigate the common UI of Sencha Architect, how to start and structure an Architect project, how to create a common mobile app with navigation pattern , how to link components, how to bind data, how to deploy generated code in your project, the difference in prototyping levels and the fit of rapid prototyping tools such as Architect, and we are going to have fun creating a real working mobile app.

Mobile but Secure

HTML5 CSS3 applications are becoming increasingly popular for mobile platforms. An assortment of applications makes use of the mobile devices to run, but when it comes to mobile security it is a wild west, the next frontier. What can you do to create mobile or web apps with a more solid security stance and prevent your mobile software from becoming another way to hack into your servers, your customers’ data? If you don’t want to be in the news as the next mobile app whose weakness a hacker group used to get sensitive data you should familiarize yourself with the mobile security tips and tricks.
We will take a look at the mobile security top ten and discuss the juiciest elements such as   Insecure Data Storage, Weak Server Side Controls, Insufficient Transport Layer Protection, Client Side Injection, Poor Authorization and Authentication and ways and how to mediate them effectively.

So there you have it.
Hope to see you at the Mall of America in May !

Cheers,
B.

CF: Scheduled Task Security venerability in Adobe CF

Getting hit by a security vulnerability is no fun. This new one using CFIDE scheduling (http://www.adobe.com/support/security/advisories/apsa13-01.html) seems to have impacted quite a few users.
This all happened around Christmas and went downhill from there most likely automatic network scanning of availability of a certain URL path followed by automated attack.

Charlie Arehart has several blog post on this topic so I am not going to expand too much:

The old adage to lock down your server still holds but is no solace to the people that got hit since it would impact a fully patched server.
The short of this is to disable access to certain CFIDE paths. This may be the practice that you already follow, then kudos!, more importantly, get the word out to other users. Let your friends know so they don’t fly blind.

Overall I am surprised by this vulnerability since it seems to originate from a vector that, in my mind, should require authentication or at least some sort of access control. Seemingly the scheduling of tasks is vulnerable and wide open by default. Initial thought on this: Crap!
Maybe, as a community, we should vet more closely the out of the box CFML code that is being deployed. The secure stance always has been to not deploy any example and docs on production, however, this is part of the system would be required to administer it.

Couple of things that I would like to delve deeper into:

a) Detecting Code Compromise:
In the last talk that I had presented around application security I had shown an example that can be easily implemented and allows users to detect any code change on the machine.
The idea is to generate an application signature that, then, can be verified to see whether the application is still consistent with what you published.
This is a simple version, you may expand and adjust. This will help you detect whether you have been compromised.
You can do this against the CFIDE, Customtag, and/or any other directory you store code in. You can create separate signatures (i.e. modules) or combined ones.

The goal is to quickly be able to tell whether you were hit by a zero-day vulnerability or anything else made its way onto your server that you did not expect.

It involves two general steps:

First run a recursive cfdirectory on your website/app and, second, create a hash from it.
Something like this:

<cfdirectory action="LIST" directory="c:\inetpub\wwwroot" 
  name="myAppFiles" filter="*.cf*" recurse="Yes">

<!--- build md5 hash  --->
<cfset myAppSig = Hash(SerializeJSON(myAppFiles),"MD5","us-ascii")>

Then compare the generated application signature against a last known good one. This can be done on App start, or a scheduled basis, even add hock if you app is small.

Here is the similar sample that stops application execution if compromised if you include it in your OnApplicationStart function:

<cffunction name="OnApplicationStart">
 <!---
 This assumes that you have a database with a
 table named "appcheck" that has at least two fields
 id =    auto number/indexed
 value = text (20)
 
 The first time the app starts, the valid
 application signature will be determined.
 There after the application will abort if 
 the signature does not match.
 
 To publish new code.
 Clear the appcheck table or insert 
 the new valid signature as last row. 
 --->
 
 <cfquery name="selCheck">
  SELECT value
  FROM appcheck
  WHERE id = (SELECT Max(ID) FROM AppCheck) OR id=0
 </cfquery>
 
 <!--- determine app check crossum --->
 <cfdirectory name="selAppFiles" action="list" filter="*.cf*" 
  directory="#getComponentPath()#" recurse="Yes">
 <cfset strJson = SerializeJSOn(selAppFiles)>
 <cfset md5Hash = Hash(strJson,"MD5","us-ascii")>
 
 <cfif selCheck.RecordCount>
  <!--- application compromise check. 
        We could email someone automatically, 
        raise alarms, call the cops --->
  <cfif md5Hash neq selCheck.value>
   Application compromised.<br/>
   <cfoutput>
   #md5Hash# -- #selCheck.value#
   </cfoutput>
   <cfabort>
  </cfif>
 </cfif>

 <cfquery name="insCheck">
  INSERT INTO AppCheck 
  (VALUE) VALUES ('#md5Hash#')
 </cfquery>  
</cffunction>

b) Preventing people from using the vulnerability:
Charlie and other have done a good job of summarizing the source and ways you can prevent the exploit. It all seems to boil down to not let users hit certain CFIDE paths:

  • /CFIDE/administrator
  • /CFIDE/adminapi
  • /CFIDE/componentutils

The bad thing is that using IIS/Apache facilities to lock out call URLs may not be good enough. There is the question of virtual paths and ColdFusion built in webservers that can bypass your effort. In addition, the normal Adobe CF connection between IIS and CF is using a wildcard based handling of all request. That means all requests to your website are first routed to CF/connector, even, if you ask for an image. If you know me you know I am biased, but obviously this blows big time, since it causes unnecessary processing on IIS and CF. Try to pause or stop CF and call a static HTML page on IIS or Apache, you will wait for a while. Enough of the ranting! In short, please test your lockout configs after you make changes to make sure that everything is locked out as it should be.

If you are using ColdFusion 10 on IIS you also have the option to deploy my BonCode connector. It does not block static page access, or hinder non CF processing. Since its first version it had the ability to block access to administration pages; you will not be able to bypass this even if you have the built in CF webserver running (yes, you can still hit the built in webserver if you bypass IIS altogether but that would require deliberate foolishness where you have opened the non-standard port on your Windows OS and firewalls everywhere).

Here is a blog post on how this feature is used to secure Railo administration pages. This method also works for OpenBD, and Adobe CF10.
The installation for CF10 is not out of the box, if there is interest in this I will provide it in later versions. Also this is not officially supported by Adobe, though given the system instability issues and problems that have occurred with the supplied connector you may want to consider testing anyway.

Cheers,
B.

CF: ColdFusion with Amazon Load Balancer (ELB) and mysterious line breaks causing “unterminated string literal” exceptions

This seems like a complicated scenario at first until I started thinking about it again.
You use ColdFusion in the Cloud, specifically in the Amazon cloud, you then add a load balancer in front of your servers, then you notice your JavaScript starting to error out in some browsers. You cannot explain it.
Then, you spent countless hours going nuts.

Here is a sample JavaScript and ColdFusion block that would break. The simple block retrieved a name from a ColdFusion function and passed it to JavaScript:

<cfoutput>
  <script type=”text/javascript”>
    var myName = ‘#fCallForName()#’;
  </script>
</cfoutput>

The above returned this in browser lets say the name was “John” :

  <script type=”text/javascript”>
    var myName = ‘
John’;
  </script>

This, in turn, throws a “unterminated string literal” exception in JavaScript because of the line break right before the name. Of course, you say, silly you, you probably doing something in CF to return a Newline character combination. Good thought! So I changed the CF side to be as simple as possible. Here is the CF code that returns an empty string; guaranteed!

<cffunction name=”fCallForName” returntype=”string”>
<cfreturn “”>
</cffunction>

So nope, empty string still produced new line in the JavaScript output. So a few brain cycles go by and
I get to thinking to check to hit servers directly (bypassing amazon ELB altogether) and see what is returned. The return this time is enlightening, there is a blank string rather than the expected empty string, but no newline.

  <script type=”text/javascript”>
    var myName = ‘ ‘;
  </script>

Yeah, progress, maybe? This is different, which again it should not be. Thus, the only thing this establishes is that the Amazon load balancer (ELB) is changing the output stream somehow. Then, I also remembered an earlier blog post of mine where I outlined that CF inserts an empty space character when the function output is used directly in place in HTML context:  /cf-coldfusion-functions-and-case-of/
… and the fog began to lift.

I immediately reassigned the function output to a variable like so:

<cfset userName = fCallForName()>

Then, used the variable to output the content of the function:

<cfset userName = fCallForName()>
<cfoutput>
  <script type=”text/javascript”>
    var myName = ‘#userName#’;
  </script>
</cfoutput>

…and bingo, everything started to work again. No more JavaScript errors. However the conclusions here are more scary then the error:

a) ColdFusion does introduce more than just a space character when function output is used in place
b) Amazon elastic load balancer makes modifications (corrections?) to the network stream and changes the output. For each in place output of function call it adds a line break.

Both a) and b) should not happen, but they do ;o(
At least now my pain could be your gain. Cloud pittfals, I guess…simple assumptions like surely the load balancer will not modify my data could throw you off .

Cheers,
B.

CF: CFCamp 2012 more of everything

CFCamp 2012 is over. It was another whirlwind affair with more people, more speakers, more topics and more sponsors.
Overall a good gathering that had some nuggets to take away and think about.
Thanks everyone who stayed for my late presentation on practical application security.

You can download presentation slides if you want to review them at your leisure.

Cheers,
B.

CF: CFCamp here we go

Another year, another CFCamp.
The ColdFusion faithful trek into the southerly realms of Munich, Germany to learn all about the intricacies of living the code – dream ;o) So I am my way to join the fine folks, raise a Stein, and catch up on all that is newsworthy.
There will be a selection of topics on HTML5 and mobile since this is a pattern that has been steadily gaining ground among CF’ers and is reflected in this conference.
In addition there is a smaller CFAcademy segment with expanded hands on training.
If you are participating in Security section of CFAcademy watch this blog as I will post some course materials shortly.

CFAcademy users please download your exercise materials.
You will need :

  • One of the CFML engines (Adobe, Railo, OpenBD) installed. I will use Railo to work through examples.
  • A database: MySQL
  • Create a data source “book1” using the sql dump.

Cheers,
Bilal

CF: MXunit Automatically Generating Tests and the Challenge of Test Scope

Let me start by saying that I like unit testing . It is very valuable tool in the arsenal of developers to fight the ever present monsters of recursion bugs and integration nightmares.
However, I will also freely admit that there is no agreement on how many unit tests are ever enough. Put a three developers in a room and you will get three different answers and maybe a headache to boot.

Also, in my case, the other challenge was to determine whether I had sufficient permutations of tests to verify that unit test goals could be met. Mind you that, test-coverage does not equal code coverage, but it probably is a good proxy. So, another goal of mine is to be able to think through all kinds of ways to call on code even the bad stuff and ensure that it behaves as expected. There is system in this, however, and this is what we use come up with most of the tests.

With MXUnit the world of ColdFusion has had a sturdy companion to write all these nice unit tests, however, we needed more. We wanted to have a good number of  tests especially for cfc (ColdFusion components). Coming up with the variety and permutation of tests does require quite a bit of hand coding, so we were looking for an easier way.

The solution we came up with was to generate test stubs. Many, many, many test stubs. We then review the cases and expand the ones we think cover the objective sufficiently. We end up deleting quite a few generated test, but this gives us a good baseline, especially for things we don’t commonly unit test but we should, e.g. sending in a number when a string is expected, sending complex values, when simple ones will do and vice versa.

This may not be the way you want to work all test cases but helps to take care of many. We look at a functions parameters and generate all combinations of parameters with standard and break values. This can amount to be many thousandths so use with care.

The generator sample code I am attaching has been squeezed into one code file (cfm template) so it is easier to post. Place the code content into a file named “GenerateTestsPublic.cfm” under your mxunit path. Not optimal but the concepts should be visible. Take it for a spin and make adjustments. Feel free to add comments to the blog post.

Happy Experimenting:

GenerateTestsPublic.cfm:

<!--- 
 Generate mxUnit tests given a component name.
 One File per component function will be generated in this directory.
 
 Will attempt to generate all permutations possible.
 Random values
 Break Values
 
 Prefix:
 Test_[componentName]_[functionName].cfc
 
 An overall TestSuite will be generated
 
 TestSuite_[componentName].cfm
 
 Distributed under Apache 2 Lincese
 (c) 2012 Bilal Soylu 
  --->




<!DOCTYPE HTML>

<html>
<head>
 <title>Generate MXUnit Test for Components</title>
</head>

<body>

<CFIF IsDefined("Form.objectName") AND Trim(Form.objectName) NEQ "">
  
 <!--- get object info  --->
 <cftry>
  <cfset objReg = CreateObject("COMPONENT","#Trim(Form.objectName)#")>
  <cfset stcMeta = getMetaData(objReg)>
 
  
  <!--- generate directory  --->
  
  <!--- generate test files  --->
  <!--- save object name  --->
  <cfscript>
   //save main object
   strName = UCase(Trim(Form.objectName));
   strUseObjectNameInDir = ReplaceNoCase(strName,".","_","ALL");
   strDirPrefix = "unitTests";
   strHint="";
   arrTestCaseNames = [];
   //max test cases (high number of combinations can exist
   intMaxCases = -1;
   intMaxCasesPerFile = 1000;
   if (IsDefined("Form.maxTests") and Val(Form.maxTests) GT 0) intMaxCases = Val(Form.maxTests);
   if (IsDefined("Form.maxTestsPerFile") and Val(Form.maxTestsPerFile) LT intMaxCases) intMaxCasesPerFile = Val(Form.maxTestsPerFile);
   //names & paths
   strBasePath = GetDirectoryFromPath(GetCurrentTemplatePath()) & strDirPrefix & "\";
   strFilePrefix = "Test_#strUseObjectNameInDir#_"; 
   strTestSuiteName = "TestSuite_#strUseObjectNameInDir#.cfm";
   strTestSuitePath = strBasePath & strTestSuiteName;
   strTestCaseDirName = "TestCases_#strUseObjectNameInDir#";
   strTestCaseDirPath = strBasePath & "" & strTestCaseDirName;
   blnComplete=false;
   crlf = chr(13) & chr(10);
   tab = chr(9);
   
   //create directory
   if (NOT DirectoryExists(strTestCaseDirPath)) DirectoryCreate(strTestCaseDirPath);
   
   //sample data
   sampleStructure= ":" & SerializeJSON({"number"=9999,'text'='my text value','dte'=Now()});
   sampleArray= ":" & SerializeJSON(["a","b",33,{"number"=9999,'text'='my text value','dte'=Now()}]);
   sampleQuery = QueryNew("");
   FastFoodArray = ["French Fries","Hot Dogs","Fried Clams","Thick Shakes"];
   nColumnNumber = QueryAddColumn(sampleQuery, "FastFood", "VarChar", FastFoodArray);
   
  
   
  </cfscript>
  
  
  <!--- assemble files  --->
  <cfif IsDefined("stcMeta.Functions") AND ArrayLen(stcMeta.Functions) GT 0>
   <cfloop index="i" from="1" to="#ArrayLen(stcMeta.Functions)#" step="1">
    <!--- each functions has its on test case. init var containers  --->
    <cfset stcFunc = stcMeta.Functions[i]>
    <cfset selParaCombinations = QueryNew("")>
    <cfset stcQueries = {}>
    <cfparam name="stcFunc.Access" default="public">
    <cfif stcFunc.Access IS "public">
     
     <cfset strMethod = UCase(stcFunc.name)>
     <cfset strMethodHint = "">
     <cfset strTestCaseName = strFilePrefix & strMethod>
     <cfset strTestCaseFileName= strTestCaseDirPath & "\#strTestCaseName#.cfc">
     
     <!--- output (bsoylu 03-29-2012) --->
     <cfoutput>
     <br>processing: #strMethod#<br>
     </cfoutput>
     
     <!--- start testCase tC variable  --->
     <cfset genStartTc()>    
     
     <!--- init  --->
     <cfset arrParams = stcFunc.Parameters>     
     
     
     <!--- iterate through each paramter and set base test values  --->
     <cfif (ArrayLen(arrParams) GT 0)>
      <cfset strQList = "">
      <cfloop from="1" to="#ArrayLen(arrParams)#" index="y">
       <cfset stcPara = arrParams[y]>       
       <cfset strParaName= UCase(stcPara.Name)>
       <!--- create query and query handle  --->
       
       <cfset stcQueries["q_#strParaName#"] = QueryNew(strParaName,"CF_SQL_VARCHAR")>
       <cfset selQ = stcQueries["q_#strParaName#"]>
       <cfset strQList = ListAppend(strQList,"q_#strParaName#")>
       
       <!--- if parameter is not required it can be null as a valid state  --->      
       <cfif NOT (IsDefined("stcPara.Required") AND stcPara.Required)>
        <cfset QueryAddRow(selQ)>
        <cfset QuerySetCell(selQ,strParaName,"NULL")>        
       </cfif>
       <!--- by type  --->
       <!--- we prefix with equal sign if we need to eval the data later in processing --->
       <!--- we prefix with colon (:) when we need to deserialize JSON  --->
       <cfif IsDefined("stcPara.Type")>
        <cfif stcPara.Type IS "numeric">
         <!--- for each numeric: use max, use min, use zero, use random  --->
         <!--- Java Long: 9223372036854775807  and -9223372036854775808  --->
         
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"999999999")>         
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"-999999999")>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"0")>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"#RandRange(0,999999999)#")>         
        </cfif>
        
        <cfif stcPara.Type IS "string">
         <!--- for each string: use max, use empty, use "coldFusion"  --->
         <cfset strLongString = RepeatString("a",4000)>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"#strLongString#")>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"")>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"coldFusion")>         
        </cfif>
       
        <cfif stcPara.Type IS "struct" OR stcPara.Type IS "any" >
         <!--- for each struct: use empty, use test struct  --->
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,sampleStructure)>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"=StructNew()")>         
        </cfif>
        
        <cfif stcPara.Type IS "date" >
         <!--- for each date: use "1/1/1980" use today use tomorrow, use 12/31/2200  --->
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"=CreateDate(1980,1,1)")>          
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"=Now()")> 
         <cfset tomorrow = DateAdd("d",1,Now())>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"=CreateDate(#Year(tomorrow)#,#Month(tomorrow)#,#Day(tomorrow)#)")> 
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"=CreateDate(2200,12,31)")>                 
        </cfif>        
       
       
        <cfif stcPara.Type IS "array" >
         <!--- for each array: add sample array and empty  --->
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"=ArrayNew(1)")>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,sampleArray)>                                 
        </cfif>  
        
        <cfif stcPara.Type IS "boolean" >
         <!--- for each bool: true/false  --->
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"Yes")>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,"No")>                                 
        </cfif> 
        
        <cfif stcPara.Type IS "query" >
         <!--- for each query: sample and empty --->
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,":#SerializeJSON(sampleQuery)#")>
         <cfset QueryAddRow(selQ)>
         <cfset QuerySetCell(selQ,strParaName,":#SerializeJSON(QueryNew(""))#")>                                 
        </cfif>                 
               
       <cfelse>
        <!--- valid state for non-typed or unknown paramters  --->
        <!--- for each undefined type (any): use structure  --->
        <cfset QueryAddRow(selQ)>
        <cfset QuerySetCell(selQ,strParaName,sampleStructure)>       
       
       </cfif>
      
       <!--- for the first parameter query we also set the query result. If there is only one parameter this is what will be returned --->
       <cfif y IS 1>
        <cfset selParaCombinations = selQ>
       </cfif>
      </cfloop><!--- loop through paramters  --->
     
     
      
      <!--- build cartesian product of queries (this will show us all the needed combinations  --->
      <cfset qCounter = 0>
      <cfloop list="#strQList#" index="qName">
       <cfset qCounter ++> 
       <cfif qCounter GT 1>
        <!--- combine into cartesian set. We can only do two queries at a time  --->
        <cfset qToAdd = stcQueries[qName]>
        <cfquery name="selParaCombinations" dbtype="query">
         SELECT DISTINCT *
         FROM selParaCombinations,qToAdd   
        </cfquery>       
       </cfif>  
          
      </cfloop>
      <!--- determine max loop (bsoylu 03-30-2012) --->
      <cfset intMaxLoop = selParaCombinations.RecordCount>
      <cfif intMaxCases GT 0>
       <cfset intMaxLoop = intMaxCases>
      </cfif>
      
      <cfoutput>
       found #selParaCombinations.RecordCount# test combinations 
       <cfif intMaxCases GT 0 AND intMaxCases LT selParaCombinations.RecordCount>
        <b>only #intMaxCases#</b> will be generated      
       </cfif>
       <br>
      </cfoutput>
      <cfflush>
      <!--- loop and generate test for file (bsoylu 03-29-2012) --->
      <cfset intTestCount = 0>
      <cfset intFileCount = 0>
      <cfloop query="selParaCombinations" endrow="#intMaxLoop#"> 
       <cfset tf="">
       <cfset intTestCount ++ >
       <!--- create para structure (bsoylu 03-29-2012) --->
       <cfset stcPara = {}>
       <cfloop list="#selParaCombinations.ColumnList#" index="idxCol">        
        <cfset strVal=Evaluate("selParaCombinations.#idxCol#")>
        <!--- check whether we need to further process the content (bsoylu 03-29-2012) --->
        <cfif strVal neq "NULL">
         <cftry>
          <cfif strVal is "">
           <cfset stcPara[idxCol] = strVal>
          <cfelseif Left(strVal,1) IS ":">
           <cfset stcPara[idxCol] = DeserializeJSON( Right(strVal,Len(strVal)-1))>
          <cfelseif Left(strVal,1) IS "=">
           <cfset stcPara[idxCol] = Evaluate( Right(strVal,Len(strVal)-1))>
          <cfelse>
           <cfset stcPara[idxCol] = strVal>
          </cfif> 
          
          <cfcatch>
           <hr>
           <cfoutput>could not interpret #Right(strVal,Len(strVal)-1)#</cfoutput>
           </hr>
           <cfabort>
          </cfcatch>
         </cftry> 
        </cfif>      
       </cfloop>
      
      
       
       <!--- create function for this para combination (bsoylu 03-29-2012) --->
       <cfset strJSON = SerializeJSON(stcPara)>
       <cfset tf=tf & crlf>     
       <cfset tf=tf & crlf & tab & '<cffunction name="test_#strMethod#_#NumberFormat(selParaCombinations.CurrentRow,"00000")#" >''>
       <cfset tf=tf & crlf & tab & tab & '<cfset var strJSONPara ='''  & strJSON &'''>''>
       <cfset tf=tf & crlf & tab & tab & '<cfset var response ="">''>
       <cfset tf=tf & crlf & tab & tab & '<cfset var argCol = DeserializeJSON(strJSONPara)>''>
       <cfset tf=tf & crlf & tab & tab & '<cfinvoke argumentcollection="##argCol##" component="#strName#" method="#strMethod#" returnvariable="response"/>''>
       
       <cfset tf=tf & crlf & tab & tab & '<cfset assertNotEquals( "",response,SerializeJSON(response) & " - failed call with' & Replace(strJSON,'"','""','ALL') & '")>''>
       <cfset tf=tf & crlf & tab & '</cffunction>''> 
       <cfset tf=tf & crlf>     
      
       <!--- add to test file content (bsoylu 03-29-2012) --->
       <cfset tc=tc & crlf & tf>
       
       <cfif intTestCount mod intMaxCasesPerFile IS 0>
        <!--- write what we have so far (bsoylu 04-03-2012) --->
        <cfset intFileCount++>
        <cfset strModTestCaseName = strTestCaseName & "_File" & intFileCount>
        <cfset tC = tC & crlf & '</cfcomponent>''>
        <!--- set numbered File names (bsoylu 04-03-2012) --->
        <cfset strModTestCaseFileName= strTestCaseDirPath & "\#strModTestCaseName#.cfc">
        <cfset ArrayAppend(arrTestCaseNames,strModTestCaseName)>
        <cffile action="WRITE" file="#strModTestCaseFileName#" output="#tc#" addnewline="No">
        
        <cfoutput>generated sub case file: #strModTestCaseName#<br></cfoutput>
        <!--- reset tc for next file (bsoylu 04-03-2012) --->
        <cfset genStartTc("_File" & intFileCount + 1)>
       </cfif>
       
       
      </cfloop> <!--- set parameters (bsoylu 03-29-2012) --->
      
     </cfif> <!--- we have parameters  --->
     
     <!--- complete the last file (bsoylu 04-03-2012) --->
     <cfset tC = tC & crlf & '</cfcomponent>''>     
     <cfset ArrayAppend(arrTestCaseNames,strTestCaseName)>
     <!--- write test case  --->
     <cffile action="WRITE" file="#strTestCaseFileName#" output="#tc#" addnewline="No">     
     <cfoutput>generated last case file: #strTestCaseName#<br></cfoutput>
    </cfif> <!--- public function  --->

    </cfloop> <!--- loop through functions  --->
   
   <!--- generate test suite file  --->
   <cfset genTestSuite()>
   <cfset blnComplete = true>
  </cfif>
  
  <hr>
 
 
  <cfcatch type="Any">
   <font color="red" size="+2">
   There was an error. Please ensure that your component is located in the correct directory: <BR>
   </font>
   <table cellspacing="2" cellpadding="2" border="1">
    <tr>
     <td><cfdump var="#cfcatch#">    
     </td>
    </tr>    
    </table>
   <HR color="#ff0000" noshade>
  </cfcatch>
 
 </cftry>

 
</CFIF>


<cffunction name="genTestSuite">
 <cfset var tcName ="">
 <cfset tf = crlf> 
 <cfset tf=tf & crlf & '<cfparam name="URL.output" default="extjs">''>
 <cfset tf=tf & crlf & '<cfset testSuitePath = "mxunit.framework.TestSuite" >''>
 <cfset tf=tf & crlf & '<cfset testSuite = createObject("component", testSuitePath).TestSuite() >''>
 
 <cfloop from="1" to="#ArrayLen(arrTestCaseNames)#" index="idxArr"> 
  <cfset tcName = arrTestCaseNames[idxArr]>
  <cfset tf=tf & crlf &   '<cfset uTest = createObject("component", "#strTestCaseDirName#.#tcName#")>''>
  <cfset tf=tf & crlf &   '<cfset testSuite.addAll("#tcName#", uTest) >''>
  <cfset tf=tf & crlf>
 </cfloop>
 
 <cfset tf=tf & crlf>
 <cfset tf=tf & crlf & '<cfset results = testSuite.run() >''>
 <cfset tf=tf & crlf & '<cfset out = results.getResultsOutput(URL.output)>''>
 <cfset tf=tf & crlf & '<cfif NOT IsSimpleValue(out)>''>
 <cfset tf=tf & crlf & tab & '<cfdump var="##out##">''>   
 <cfset tf=tf & crlf & '<cfelse>''>
 <cfset tf=tf & crlf & tab & '<cfoutput>'##out##</cfoutput>'>
 <cfset tf=tf & crlf & '</cfif>''>



 
 <!--- loop through and add tests (bsoylu 03-29-2012) ---> 
 <cffile action="WRITE" output="#tf#" file="#strTestSuitePath#" addnewline="No">

</cffunction>

<cffunction name="genStartTc">
 <cfargument name="strModifier" type="string" default="" hint="modifier for component name">
 
 <!--- start testCase tC  --->
 <cfset tC = '<cfcomponent displayname="MxunitTestCase_#strTestCaseName##Arguments.strModifier#" extends="mxunit.framework.TestCase">''>
 
 <!--- empty call no parameters  --->
 <cfif Arguments.strModifier IS "">
  <cfset tc=tc & crlf & tab & '<cffunction name="testNoParams_#strMethod#" >''>  
  <cfset tc=tc & crlf & tab & tab & '<cfset var response ="">''>  
  <cfset tc=tc & crlf & tab & tab & '<cfinvoke component="#strName#" method="#strMethod#" returnvariable="response"/>''>
  <cfset tc=tc & crlf & tab & tab & '<cfset assertNotEquals( "",response,"No parameter call failed")>''>
  <cfset tc=tc & crlf & tab & '</cffunction>''>
 </cfif>
 
</cffunction>

<cfif IsDefined("blnComplete") and blnComplete>
 <font size="+2" color="#008000">
 <cfoutput>
 Successfully generated Test Suite for Component [#UCase(Form.objectName)#]. <BR>
 Test suite file : <a href="#strDirPrefix#/#strTestSuiteName#" target="_blank" title="run tests">[#strTestSuitePath#]</a> <BR>
 
 Please review and adjust specific test cases.
 </cfoutput>
 </font>
 <cfabort>
</cfif>

<h2>Welcome</h2>






 <BR>
 This program will help you generate mxUnit test cases.
 It requires access to components to be analyzed.
 <BR>
 
 After initial generation you should make changes as the assertion are generic.
 The objective is provide broad test coverage.
 
 <HR>
 Behavior Notes <BR>
 <PRE>
 All files will be generated in the "unitTests" subdirectory based on the initial directory this template is run from
 Will attempt to generate all permutations possible.
 Random values
 Break Values
 
 Test File Name:
 &nbsp;&nbsp;TestCases_[componentName]\Test_[componentName]_[functionName].cfc
 If multiple files are generated per function, a File[n] postfix wil be added except the last one:
 &nbsp;&nbsp;TestCases_[componentName]\Test_[componentName]_[functionName]_File[n].cfc
 
 An overall TestSuite will be generated
 Test Suite name:
 TestSuite_[componentName].cfm
 
 </PRE>
 
 <form method="post" action="GenerateTestsPublic.cfm">
 <table cellspacing="2" cellpadding="2" border="0">

 <tr>
  <td>Component Path from the webroot (e.g. component located [root]/myweb/cfcs/foo.cfc would be myweb.cfcs.foo)</td>
  <td><input type="Text" name="objectName" value=""></td>
 </tr>


 <tr>
  <td>Max Number of Test Cases per File (same function)</td>
  <td><input type="Text" name="maxTestsPerFile" value="1000"> (multiple files will be generated if more than this number)</td>
 </tr> 
 <tr>
  <td>Max Number of Test Cases for a Function</td>
  <td><input type="Text" name="maxTests" value="5000"></td>
 </tr>  
 <tr>
  <td></td>
  <td><input type="submit" name="submit" value="submit"></td>
 </tr> 
 </table>
 </form>



</body>
</html>

Cheers,
B.

CF: ColdFusion 10 on Tomcat and the CGI mistery

One of the things I applaud Adobe for in the new release of ColdFusion is to have the stomach to change the  underlying Application container. Moving from JRUN to Tomcat is the right step. However, as part of the move, there still seems to be some mystery surrounding what this means.

As pointed out previously by myself, Rupesh Kumar, has published a good primer on the differences, but also introduced some questions which I did not find answers for. So nothing like the present to go and dig.

More specifically he points out that Adobe has made changes (enhancements) to Tomcat that allow it to work hand in hand with their connectors to get more data to the servlet engine; this, according to him, would not be available if you ran a deployment of CF10 on standard Tomcat.

Of course, I wondered, what in particular would not be available and ran some tests. I used the following scenarios (all on Windows 2008 R2):

a) Standard Install of CF 10 with Adobe ISAPI connector and IIS.
b) Standard Install of CF 10 with BonCode connector and IIS.
c) Tomcat WAR deployment of CF with BonCode connector and IIS.
d) Tomcat WAR Deployment using Tomcat web server (Coyote)

I configured the BonCode connector for best alignment with Adobe as outlined in my previous blog post.
I admit that I did not try to use the Apache Tomcat  regular ISAPI redirector, since it would have not worked for scenario a) and b) and I assume that there are differences there. I also did not try Apache HTTPD webserver scenarios.

I arrived at my conclusions by using a sophisticated setup ;o)
I simply dumped the CGI scope and the count of CGI variables, then compare the dump files. The dump files in the download  zip document reflect the output for the scenarios above.

However, overall I was not able to discover large differences in the CGI scope among the different approaches for running ColdFusion 10. The results for scenarios a) – c) were identical, and while scenario d) showed differences, they were minimal. I attribute this to Tomcat’s native web-server’s interpretation of HTTP protocol. It chooses to interpret traffic and headers slightly differently.

The differences in scenario d) relate to the following  CGI variables:
Content_Length
Context_Path
Gateway_Interface
HTTPS

Thus, if you wish to keep your coding the same, select options a)-c) while exercising some care in the use of Apache Coyote as webserver for ColdFusion 10.

Cheers,
B.