Sep 13, 2016

ColdFusion 9 Install on RedHat with JBoss

Just wanted to pass along some notes from doing a ColdFusion 8 upgrade to ColdFuison 9 64 bit JBoss install on RedHat this week.
 
First off, all went fairly well, but I think you might find the tips below helpful.
 
First get all the software:<br />
- CF 9  Linux 64 bit<br />
- CF 901 Updater 64 bit <br />
- CF 9 Cumulative Hot Fix  (optional)
 
The ColdFusion Installation docs are what we generally followed:<br />
<a href="http://help.adobe.com/en_US/ColdFusion/9.0/Installing/WSc3ff6d0ea77859461172e0811cdec18c28-7fbd.html" target="_new">Deploying ColdFusion 9 on JBoss Application Server</a>
<br />
You can get the Updater and the Hot Fixes following the below links:<br />
<a href="http://www.adobe.com/support/coldfusion/downloads_updates.html" target="_new">ColdFusion Updaters</a><br />
<a href="http://kb2.adobe.com/cps/862/cpsid_86263.html" target="_new">ColdFusion 901 Cumulative HotFix 1</a>
 
Install CF9 using the J2EE install option.
 
Once installed you are left with the decision to test the install, which would put you down the road of expanding the ear and war per the instructions.  However, the 901 updater executable requires you to apply it to an archived war file.   So, you might want to apply the updater before expanding the ear and war and moving the resulting ear directory into JBoss's deploy directory.
 
We did not know the CF 9.01 updater required an archive, and we had already deleted the war file, so we had to recreate the war, apply the updater, re-exand and swap out the new war with the old war directory.  So, just keep that in mind if your doing a complete install as we were doing.
 
Here are the complete docs for installing the CF 9.01 Updater:<br />
<a href="http://www.adobe.com/support/documentation/en/coldfusion/901/cf901install.pdf" target="_new">Installing ColdFusion 9.01</a>
 
Updating your Settings from ColdFusion 8 to ColdFusion 9
<br />
So to move the settings we followed the instructions inside the link below:<br />
<a href="http://help.adobe.com/en_US/ColdFusion/9.0/Installing/WSe9cbe5cf462523a0-b18e31f121c8f9f003-8000.html" target="_new">Migrating ColdFusion Settings for J2EE installations</a>
 
So by following the above we did the following:<br />
- copied all the neo-* files from ../web-inf/cfusion/lib out of the 8 install, and placed then in the ../web-inf/cfusion/lib/cf8settings directory inside 9 <br />
- adjusted the runmigrationwizard and the migratecf8 settings in ColdFusion 9 cfusion/lib/adminconfig.xml, change them to true <br />
- restarted ColdFusion and entered the Admin to trigger the Migration
 
All the ColdFusion 8 settings were brought over.  The migration wizard allows you to filter out settings if you don't necessarily want them all.
 
That was it, all went well, and all the settings were brought over and all datasources connected.

Using CFImage to create custom map icons for Google Maps

In writing a map application with ColdFusion I needed to create some custom icons and discovered an easy way to take any small icon and create hundreds of them with sequential numbers overlayed on top of the icon.
 
There are a number of sites with free icons, but they all seem to stop at 99, I needed to go into the hundreds in some cases, which made me start to look at what cfimage can do for me.
 
First, I went I downloaded a default Google icon as my base (20x34px), then I created new blank icons with differing colors within Photoshop.  I saved all the blank icons in a directory called /icons off my root directory. Once I have all the blanks I then edit the list in the below script. 
 
The script will create a directory for each color, so for blue you will have /icons/blue/mapicon1.png thru mapicon99.png or however you create.
 
I think you could move the ImageNew out of the loop and into a variable but it works as written and is a one time conversion.
 
My next idea is to have user generated icons, by having the users select the background and text color.
 
I also want to create multiple lists to change the text color, since some backgrounds need black text.
 
This should be enough to get you started.  Enjoy.
 
 
<xmp>
<cfset whitecolorlist = "black,blue,blueball,brightgreen,chestnut,chocolate,cyan,darkblue">
<cfset currentDir = getdirectoryfrompath(getbasetemplatepath())>
<cfoutput>
<!--- Loop colors and create icons inside relative directory --->
<cfloop list="#whitecolorlist#" index="coloritem">
    
    <cfset iconcolor = coloritem>
    <cfset currentcolordirectory = currentdir & iconcolor>
    <!--- create a directory for the color if it does not exist--->
    <cfif not directoryexists(currentcolordirectory)>
        <cfdirectory action = "create" directory = "#currentcolordirectory#" >
    </cfif>
    
    
    <cfloop  from="11" to="99" index="i">
        <cfset myImage=ImageNew("blank#iconcolor#.png")> 
        <cfset ImageSetDrawingColor(myImage,"white")> 
        <cfset attr = StructNew()> 
        <cfset attr.style="bold"> 
        <cfset attr.size=9> 
        <cfset attr.font="verdana"> 
        <cfset attr.underline="no"> 
        <!--- Need to play with x offset depending how many characters.--->
        <cfif i lt 10>
            <cfset x = 7>
        </cfif>
        <cfif i gt 9 and i lt 100>
            <cfset x = 4>
        </cfif>
        <cfif i gt 99 and i lt 1000>
            <cfset x = 1>
        </cfif>
        <cfset ImageDrawText(myImage,i,#x#,12,attr)> 
        <!--- Place image in a directory based on color --->
        <cfimage source="#myImage#" action="write" 
destination="#currentcolordirectory#/mapicon#i#.png" overwrite="yes"> 
        
        <img src="/icons/#iconcolor#/mapicon#i#.png"/>
    </cfloop>
 </cfloop>
</cfoutput>
</xmp>

Using Google Maps to populate your form Latitude and Longitude

I needed to have a user easily enter a latitude and longitude where there might not be a true address, so I created a simple script to populate form variables when a user doubleclicks a Google Map.
 
The below is just javascript and html which uses the Google Map API V2 to place a map on the page which by default centers the map when you doubleclick.
 
The function that is fired in a dblclick event will then set the 2 form variables and leave a marker behind to help the user know where they clicked.
 
You will need to change the center points and the form names, but all this could be dynamically set as well.
 
Hope you can use this in your apps.
 
 
Note: you will need to fix the 2 InvalidTag's with script.  
<xmp>
<InvalidTag type="text/javascript">
// JavaScript Document
var cm_map;
var centerLon = '-81.655651';
var centerLat = '30.332184';
var zoomLevel = 12;
var latforminput = '';
var lngforminput = '';
var marker;
function cm_load() {  
    // create the map and display in div name cm_map
    cm_map = new GMap2(document.getElementById("cm_map"));
    cm_map.addControl(new GLargeMapControl());
    cm_map.addControl(new GMapTypeControl());
cm_map.setCenter(new GLatLng( centerLat, centerLon), zoomLevel);
// initialize vars for form fields to populate
latforminput = document.getElementById("latinput");
lngforminput = document.getElementById("lnginput");
// On Dblclick set form fields and leave a marker
GEvent.addListener(cm_map, "dblclick", function(x,ll) {
latforminput.value = ll.lat();
lngforminput.value = ll.lng();
marker = new GMarker(ll, {draggable: false});
cm_map.addOverlay(marker);
    //alert('lat:' + ll.lat() +  ' lon:' + ll.lng() );
  });
}
setTimeout('cm_load()', 500);
</script>
<!---  Get Google Map API V2 and set Google key--->
<InvalidTag src="http://maps.google.com/maps?file=api&v=2&key=ENTERYOURGOOGLEMAPKEYHERE"
 type="text/javascript"></script>
<!--- Map Div --->
<div id="cm_map" style="width:300px; height:300px">
</div>
<!--- Sample Form with values that will be populated when user Double Clicks on map--->
<form name="setlatlng">
Lat: <input type="text" name="lat"  id="latinput" value=""> <br />
Lng: <input type="text" name="lng"  id="lnginput" value="">
</form>
</xmp>

Simple way to capture your CFCatch structure

Often times we get cases where we need the complete stack trace of an error and not what is reported by any custom error handler template.
Here is a great way to track your errors that also allows inspection of the entire cfcatch structure at a later time, without interfering with any custom templates.
<code>
<cftry>
<cfinclude template="asdfasdf.cfm">
<cfcatch type="any">
<cfdocument filename="errorreport#gettickcount()#.pdf" format="pdf">
    ColdFusion Error<br />
    <cfdump var="#cfcatch#">
</cfdocument>
</cfcatch>
</cftry>
</code>
In the above code I create a missing template exception, then have the cfcatch simply use cfdocument with a unique filename, to dump out the cfcatch to a PDF file.
You can adjust the location etc as you see fit of course.
The result is a PDF for every occurence showing the complete error and hopefully allowing you to quickly get to a resoluiton.

Use this technique for troubleshooting purposes,  you would not want to start to create hundreds or thousands of documents over a short period of time.

Getting Zillow Information from ColdFusion

I recently needed to create some ColdFusion code to call into Zillow so I thought it would be worth sharing.
 
The first thing you will need is an ID from Zillow to give you access to the http services.  They use the term web services but they are really http services.  You can find out everything you need to know about getting started and the Zillow APIs here: <a href="http://www.zillow.com/howto/api/APIOverview.htm" target="_new"> Getting Started with Zillow</a>
 
Once you have your ID you can enter it into the below code and test it on your site.
 
I posted a demo here: <a href="http://www.supportobjective.com/demos/zillowtest.cfm" target="_new">DEMO</a>
 
<code>
<h1>Zillow Zestimate</h1>
<cfparam name="form.address" default="671 Lincoln Ave"  >
<cfparam name="form.city" default="Winnetka">
<cfparam name="form.state" default="IL">
<cfparam name="form.Zip" default="60093">
<cfoutput>
<form name="zform"  method="post" >
Address: <input type="text" name="address" value="#form.address#" size="35"/>
    City: <input type="text" name="city" value="#form.city#" size="20"/>
    State: <input type="text" name="state" value="#form.state#" size="3"/>
    Zip: <input type="text" name="Zip" value="#form.Zip#" size="14"/>
    <input type="submit"  name="getzestimate" value="Get Zestimate" />
</form>
 
<cfif isdefined("form.getzestimate") >
<cfset hz = 'na'>
<cfset lz = 'na'>
<cfset csz = form.city & '+' & form.state & '+' & form.zip>
    <cfset zurl = 'http://www.zillow.com/webservice/GetSearchResults.htm?zws-id=ENTER YOUR WSID HERE&address=#urlEncodedFormat(form.address)#&citystatezip=#urlEncodedFormat(csz)#'>
    <cfhttp method="get" url="#zurl#"  result="zsearch">
    <cfset xmlResult = trim(zsearch.FileContent)>
    <cfset request.zillow = XmlParse(REReplace( xmlResult, "^[^<]*", "", "all" ) ) />
    <cfif findnocase('error',request.zillow.searchresults.message.text ) lt 1>
        <cfset h = trim(request.zillow.searchresults.response.results.result.zestimate.valuationrange.high.xmltext)>
        <cfset l = trim(request.zillow.searchresults.response.results.result.zestimate.valuationrange.low.xmltext)>
        <cfif isnumeric(h)>
            <cfset hz = dollarformat(h)>
        </cfif>
        <cfif isnumeric(l)>
            <cfset lz = dollarformat(l)>
        </cfif>
         <h3>Zestimate Range:  From #lz# to #hz#</h3> <a href='#request.zillow.searchresults.response.results.result.links.homedetails.xmltext#' 
            target="_new"><h3>View Property at Zillow.com</h3></a>
       <hr />
All the other Property Details<br />
<cfdump var='#request.zillow#'>
    <cfelse>
     Zillow could not recognize address
    </cfif>
</cfif>
</cfoutput>
 
</code>

Large forms and the new ColdFusion HashDos Security HotFix

I recently took an issue where a CF Server started to return 500 Errors after the HashDos security fix was deployed.
 
http://www.adobe.com/support/security/bulletins/apsb12-06.html
 
The issue came down to how many form elements they were submitting.  The new HashDos fix adds a new parameter which needs to be closely looked at if you think you have forms longer then 100 elements.
 
The error does not go to your error handler, and creates an error like your server is down.  
 
HTTP Error 500 (Internal Server Error): An unexpected condition was encountered while the server was attempting to fulfill the request
 
It does not leave any log entries in the ColdFusion logs.  So this can be tricky to figure out if you don't immediately relate it to the security fix. It can also be frustrating since it will come up only after submitted large time consuming forms.
 
The security kb describes how to adjust a new parameter in the neo-runtime.xml, which is NOT in the CF Admin.
 
You need to edit your neo-runtime.xml file and add a new parameter called postParametersLimit and adjust it to your largest form size. Once you change this setting to allow for your largest form you should be fine.
 
Here is the code I used to test a large form:
<code>
<cfform name="test" method="post">
<cfinput type="submit" name="submitform">
<cfloop from="1" to="110" index="i">
 
    <cfinput name="f#i#" type="text" value="#i#" /><br />
    
</cfloop>
</cfform>
</code>

Another viewpoint into ColdFusion RIAFORGE Projects

Here is something I have wanted to do for awhile, an explorer tool for all the ColdFusion Projects on Riaforge.org.  Over the last few weeks I have categorize the 980 some ColdFusion projects inside RIAFORGE to make it easier for newer ColdFusion customers to see the extensive amount of pre-built ColdFusion code that exists.
 
The explorer tool can be found here:
<a href="http://www.supportobjective.com/riaforge" target="_new" >http://www.supportobjective.com/riaforge</a>
 
As I went thru all the projects I was finding all kinds of great APIs and tools that I will definitely be looking at in the future, and I hope that it will provide you with that same experience.
 
This tool is simply meant to be an easy way to see projects by category and for you to find projects you might not have found otherwise. If anyone remembers the old Allaire CF Exchange, it was organized by category in a similar fashion as well.
 
This is not meant to replace RIAFORGE.org search,  by all mean use it for keyword searches, this is just a fun way to explore ColdFusion projects.
 
I am just getting started and I will be refining categories and adding features to this explorer as time allows.  Please contact me if you have other ideas or feedback.  I have several features I want to add over the next few months.
 
I also want to give thanks to a cool javascript layout tool called Isotope, which gave the me the look and feel for the page.  For more info on Isotope check out: <a href="http://metafizzy.co">Metafizzy</a>

ColdFusion and PDF Sticky Note Collaboration

I recently took an issue where a ColdFusion customer needed to build a feature to support their internal staff share comments on thousands of PDFs that were all scanned into their repository.  The PDFs were all flat and the elements were images as the result of the scanning process.
The solution I came up with was to create a small pdf, I am calling menubar.pdf, that I could merge into the PDF in their repository using cfpdf.  Then menubar.pdf would have the ability to submit the PDF back to the ColdFusion Server.  Included below is the small piece of code required to merge in the menubar.pdf.  I also placed the filename into the metadata which will be used when I send the PDF back to the ColdFusion Server.
<code>
<cfset pdfroot = "ENTER PDF WORKING DIRECTORY">
<cfset fname = "PDF NAME">
<cfpdf action = "merge"  name = "mergedpdf" >
<cfpdfparam source="#pdfroot#/menubar.pdf"  >
    <cfpdfparam source="#pdfroot#/#fname#.pdf" >
</cfpdf>
<cfset newkeywords = StructNew()>
<cfset newkeywords.keywords = fname>
<!--- Add filename int the keyword metadata and save. --->
<cfpdf action="setinfo" info = "#newkeywords#"  source="mergedpdf" destination="#pdfroot#/notes_#fname#.pdf" overwrite = "yes"> 
</code>
The result is a PDF that can now accept comments, and be submitted back to ColdFusion and saved on the server at anytime.
Below is the code which will capture the submitted PDF and returned the saved PDF with all the new comments.
<code>
<!--- **************************************************************
Capture a submitted PDF
To Do:
1. Handle Concurrency
2. Versioning
3. Add a security token to make sure pdf is one you delivered to client.
The PDF form needs a button which will submit the entire form to this PDF.
****************************************************************--->
<!--- User submitted a pdf --->
<cfset pdfroot = "ENTER PDF WORKING DIRECTORY">
<cfset uid =  createuuid()>
<cfif StructkeyExists(gethttprequestdata().headers, "Content-Type") and 
gethttprequestdata().headers["Content-Type"] EQ "application/pdf">
     <!--- Save the binary to a file to have the cfpdf tag read it
you might find a way to way to go straight to a pdf - I was not.--->
     <cffile action="write"  output="#gethttprequestdata().content#"  file="#pdfroot#\temp.pdf" nameconflict="overwrite">
     <cfpdf action="read" source="#pdfroot#/temp.pdf" name="temppdf">
     <cfpdf action="getinfo" source="temppdf" name="pdfinfo">
     <cfpdf action="write" source="temppdf" destination="notes_#pdfinfo.keywords#.pdf"  overwrite="yes">
</cfif>
<!--- Return the new PDF
If this is sent from a Browser the user will get the new PDF
If inside Acrobat a new window opens with new PDF. --->
<cfcontent type="application/pdf" file="#pdfroot#/notes_#pdfinfo.keywords#.pdf" reset="yes" >
</code>
The menubar can be as simple as a one button PDF.  I built the menubar.pdf with LiveCycle Designer and I saved it as an Adobe Static PDF.  The merge does if you save its with the default Adobe Dynamic PDF.  Here is a screen shot of the simple PDF.  You are looking at a PDF with one button that submits the entire PDF to the above capturepdf.cfm.
<img src="http://www.supportobjective.com/blog/images/pdfsubmit.jpg" width="650" />
So with these three files you have a start at building out a way to collaborate on PDFs using the awesome PDF sticky note commenting feature.
I would probably deliver the PDFs as view only at first, then have the user click a button to add notes, which would then deliver the PDF with the menubar.
Features that you will want to look if you implement something like this feature:
<ul>
<li>Versioning -  to be able to track changes</li>
<li>Concurrency - to protect form changes overwriting each other</li>
<li>Security Token -  to verify only PDFs you delivered are ones coming back in</li> 
</ul>
This should be enough to get you started with integrated ColdFusion and PDF Sticky Note collaboration.

ColdFusion 10 with Tomcat brings back Context Root Routing

A common approach in the distant past was to use the JRun connector to route requests to the right server based on context root mappings.  This was possible in JRun 4 Connectors up to the JRun 4 updater 2. 
 
The usual scenario was that companies would use one root folder then separate the code with application names using a /appname convention, such as adobe.com/birds or adobe.com/dogs.  For each application you would then have a CF server instance. The company could then use one web site, with one connector setup, to distribute requests to as many servers as you wanted all based on the application name.  After JRun4 Updater 2 you needed to define a separate web site and separate connector in order to handle distributing load.  I know this affected a few customers as they upgraded.
 
The good news is that this type of setup is back again using the ColdFusion 10 Apache Tomcat Connectors.
 
The keys to making this work are making edits to the Connector properties files once you have setup your initial connector.
 
After you have created your first Connector setup go into C:\ColdFusion10\config\wsconfig\1\worker.properties and add worker processes for each one of the CF Servers you want to have requests routed too.
 
The file will look like this with a connector setup for the cfusion server:
<code>
worker.list=cfusion
worker.cfusion.type=ajp13
worker.cfusion.host=localhost
worker.cfusion.port=8012
worker.cfusion.max_reuse_connections=250
</code>
You want to add the same set of settings for each server.  If you don't know the port, you can find that it in the C:\ColdFusion10\<servername>\runtime\config\server.xml file.
 
An example of a new server would look like the below:
<code>
worker.list=reports
worker.cfusion.type=ajp13
worker.cfusion.host=localhost
worker.cfusion.port=8013
worker.cfusion.max_reuse_connections=250
</code>
Once you have all your servers defined you now need to tell the Connector what mappings go to what Worker.  Let's open up the C:\ColdFusion10\config\wsconfig\1\uriworkermap.properties file.
 
The default file looks like the below:
<code>
/cfformgateway/* = cfusion
/CFFormGateway/* = cfusion
/flex2gateway/* = cfusion
/flex2gateway = cfusion
/cffileservlet/* = cfusion
/CFFileServlet/* = cfusion
/cfform-internal/* = cfusion
/flashservices/gateway/* = cfusion
/flex-internal/* = cfusion
/rest/* = cfusion
/*.cfml/* = cfusion
/*.mxml = cfusion
/*.as = cfusion
/*.cfm = cfusion
/*.cfm/* = cfusion
/*.swc = cfusion
/*.cfml = cfusion
/*.cfc = cfusion
/*.cfc/* = cfusion
/*.cfr = cfusion
/*.cfswf = cfusion
/*.sws = cfusion
/*.jsp = cfusion
/*.hbmxml = cfusion
</code>
As you can see all the mappings are going to the cfusion Worker.  We want to replicate these mappings to have them recognize our application names and be routed to the corresponding Worker setup in the workers.properties file.
 
Important notes about this file, all the mappings are case sensitive.  Also, make sure the cfusion map grouping is last if it is going to act as the default server.
 
To make this all work we need to copy the set of cfusion mappings and create new ones for all our Workers we have set up.  Here is an example set of mappings for a new server:
<code>
/www.yourserver.com/reports/cfformgateway/* = reports
/www.yourserver.com/reports/CFFormGateway/* = reports
/www.yourserver.com/reports/flex2gateway/* = reports
/www.yourserver.com/reports/flex2gateway = reports
/www.yourserver.com/reports/cffileservlet/* = reports
/www.yourserver.com/reports/CFFileServlet/* = reports
/www.yourserver.com/reports/cfform-internal/* = reports
/www.yourserver.com/reports/flashservices/gateway/* = reports
/www.yourserver.com/reports/flex-internal/* = reports
/www.yourserver.com/reports/rest/* = reports
/www.yourserver.com/reports/*.cfml/* = reports
/www.yourserver.com/reports/*.mxml = reports
/www.yourserver.com/reports/*.as = reports
/www.yourserver.com/reports/*.cfm = reports
/www.yourserver.com/reports/*.cfm/* = reports
/www.yourserver.com/reports/*.swc = reports
/www.yourserver.com/reports/*.cfml = reports
/www.yourserver.com/reports/*.cfc = reports
/www.yourserver.com/reports/*.cfc/* = reports
/www.yourserver.com/reports/*.cfr = reports
/www.yourserver.com/reports/*.cfswf = reports
/www.yourserver.com/reports/*.sws = reports
/www.yourserver.com/reports/*.jsp = reports
/www.yourserver.com/reports/*.hbmxml = reports
</code>
 
I found that the /appname was not enough to catch the request, it required the full domain.  That was disappointing since you will need to change them based on environment and also deal with case sensitivity and whether they have www. etc.
 
You can remove all the mappings your applications does not plan to use.  The mappings are worthy of a separate post themselves.
 
Once both of these files are edited simply restart your web server and test.  I have read that Tomcat does check for changes and reload them every 60 seconds, but I never tested that approach.
 
 
To test I placed a cftrace tag inside each application to tell me the server.coldfusion.rootdir to verify I was getting to the correct server. I used IIS for all my testing but I think Apache should be very similar, if not identical. 
 
There you go,  Context Root Routing, one web server with one connector to as many ColdFusion servers as you want.