list="true" to the param used for the list. See the example below that selects all employees that have a birthdate in March, April or May.
1 2 3 4 5 6 7 8 9 10 11 | |
Thanks to Ben Nadel’s post here for this crucial tidbit of information!
]]>In a nutshell, Browsersync will live reload one or more browsers for you whenever your project’s files change. It will also live reload external devices pointed at the same web application. This allows you to simultaneously and efficiently test a responsive design in multiple browsers, phones and tablets - without needing to touch or reload any of them. It also synchronizes form entries and clicks across browsers and devices. For example, with my phone and tablet connected to Browsersync, I log into the app I’m developing on the tablet, and my keystrokes and touch gestures are replicated on the phone, and I’m logged in on both. Then I start navigating in the application in Chrome on my desktop, and the phone, tablet and other browsers follow as “slaves”. Interacting with any interface causes the rest of them to follow. What an incredible tool! In the few minutes I’ve been experimenting with it, I was able to easily and quickly fix a layout issue on the tablet and phone for the login screen.
Browsersync works by wrapping your application or website in its own Node server process, on another port. You can start Browsersync on the command line, or via a Grunt or Gulp task. During startup, you specify the local url of your app, the files you want it to watch - a number of other options are possible - and then a browser window opens, on port 3000 by default, and browsersync is activated for that browser. To add others, you simply use the same url. To add external devices, on localhost:3001 there is an admin panel for Browsersync that will list the external url to use. Navigate to that external url on your phone or tablet or another computer, for me it currently http://192.168.1.103:3000/, and Browsersync is set up on these devices as well. Once you have it configured, it is really simple to use.
A quick overview on installation and configuration: Basic instructions are on the homepage https://www.browsersync.io/.
1
| |
The -g flag means global so it is available from anywhere.
There are 2 start modes, –server and –proxy. server is for static files, proxy is for applications running on a server. Here’s a simplified example starting Browsersync from the command line:
1
| |
… assuming you have your local host file set up to point your app at myproject.dev. Or you can also start Browsersync using a localhost:port url, like this:
1
| |
There are a few real world ways to do this, but what I’ve done is to create a gulpfile.js in the root of my project so I can configure Browsersync conveniently and start it with a simple gulp command after cd’ing in to my project directory. That way I don’t have to remember much. Here’s my first working gulpfile.js for a real development scenario. I have ACF (Adobe ColdFusion) installed on port 8501 on my dev laptop, and gulp.watch() is configured for an FW/1 app. I’m not sure if I actually want to watch the controllers and services directories, perhaps the views directory and CSS directory is enough. But for now I will leave it like this and see how it goes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Note carefully the syntax of gulp.watch() for multiple directories / files. It’s an array of strings separated by commas. Note also that the root of the relative paths specified is where the gulpfile.js is located.
But wait, wouldn’t it be better if I could use Browsersync to develop against both ACF and Lucee? Here’s a solution that I have working that creates named instances of Browsersync so they don’t step on each other:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
I specify separate browsers for each so that ACF and Lucee sessions don’t step on one another, and separate ports for the Lucee instance of Browsersync so they can coexist. The advantage to doing it this way is I can start both instances with a single gulp command on the command line. Unfortunately the ACF and Lucee instances of Browsersync will not mirror or ghost each other, so I will still have to click and enter test data on forms separately while developing, but this is still much better than needing to reload both browsers manually all the time.
I’ve noticed one other feature of Browsersync that I wanted to see if I could get working - injecting modified CSS into the browser to avoid reloading the DOM. I’ve now rearranged my gulpfile.js to be the following, adding a second gulp.watch() call to inject the minified bootstrap css directly into the browser, and removing the dist directory from the first gulp.watch() call so the browsers are not reloaded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |
I like how this is shaping up now. :-)
Comments or suggestions welcome!
]]>1 2 | |
Here’s what it would look like if you are using the cfqueryparam tag:
1
| |
The database column this is persisted to is defined as a decimal(4,2), so 2 digits are reserved for the whole number portion of the value and 2 digits reserved for the decimal portion of the value. Logging showed values being calculated such as 4.25 and 4.5, but these were being persisted to the database rounded to the nearest integer, but with 2 decimal places none the less, so 4.25 was persisted as 4.00 and 4.5 was persisted as 5.00. Definitely not what I wanted. What’s the point of a decimal datatype if the decimal portion is always 0?
Digging into the documentation for cfqueryparam I found a scale attribute that until today I hadn’t realized was absolutely essential to use for decimal fields like this. Scale defines the … “Number of decimal places in parameter. Applies to CF_SQL_NUMERIC and CF_SQL_DECIMAL.” Its default is 0, so my values were being rounded to 0 decimal places. After changing my code to the following:
1 2 | |
the decimal portion of the values was then persisted correctly.
So the scale attribute is absolutely essential for a decimal cfsqltype. I might have tripped over this before, I’m not sure. Perhaps this blog post will help me remember it next time. Now I should search my code to see if I’ve neglected this attribute elsewhere!
]]>The first step to get up and running is to head on over to the fw1-clj GitHub repository and follow the directions to install Boot, which is a build tool for Clojure. Next, as per the instructions on the fw1-clj GitHub repository, we run a boot command in a terminal to create a new fw1-clj app, which currently looks like this:
1
| |
(Sean has indicated this invocation may change in the future, so best to follow the fw1-clj repository instructions.)
Then, we “cd” into the application directory we specified above, in this example “myfw1app”, and start the application up like so:
1 2 | |
Specifying the PORT variable isn’t required, but may be necessary to avoid conflicts. If omitted, the default port is 8080.
Navigating to localhost:8111, we see a lovely default page that says “Framework One - Welcome to FW/1”. Yay! It certainly is impressive how easy that is. No server to install. Nothing to download. No settings to find and configure. We’re ready to start developing.
There are several options to choose from in terms of an IDE for Clojure. Many experienced Clojure developers use Emacs with CIDER, or the Cursive plugin for Intellij. Both of those have a learning curve that you may prefer to avoid to focus on getting the basics of Clojure down first.
For beginners, good IDE’s for experimentation are Light Table or Atom with a few Clojure packages installed, such as language-clojure, proto-repl and parinfer. I’m using Atom at the moment because I’m also using it for CFML development, and also because the Clojure packages seem to work very well.
One of the first things I wanted to figure out was how to visualize the rc map (or struct as it would be known in CFML). With a tip from Sean, I navigated to src/projectName/controllers/main.clj in the project that boot generated for me, and within the default function definition, I added (println rc)
1 2 3 | |
reloaded the app in the browser, and then checked the terminal window where I had started the app with the boot command for the output. Nothing was output there. After a moment, I remembered that I probably had to reload the application to get the changes picked up. Checking the somewhat sparse documentation on the Github page, I found the default reload query string would be “?reload=secret”. Hitting localhost:8111?reload=secret in the browser then generated the rc map in my terminal window - all in one very long line. After another suggestion from Sean, I replaced the print line function (println rc) with the clojure pretty print function (clojure.pprint/pprint rc)
1 2 3 | |
hit localhost:8111?reload=secret again, and now the keys within the rc map were nicely separated by line breaks in the terminal output. Much more readable that way.
I noticed within the output that the :reload-application-on-every-request key was set to false. Better in development to have that set to true, so reading the docs, I found “the call to (fw1/start) can be passed configuration parameters either as a map or as an arbitrary number of inline key / value pairs”
Ok, so opening src/projectName/main.clj, I found the call to (fw1/start) and changed it from
1 2 3 4 | |
to
1 2 3 4 | |
Then I reloaded the app with “?reload=secret”, and the rc output for :reload-application-on-every-request didn’t change. Hmmm …
After a bit of investigation, (I asked Sean again on Slack), I found that to reload changes to the configuration params, I needed to go to the terminal window where I started the app with boot run, shut the app down with control-c, and then start it up again with PORT=8111 boot run.
Checking the rc output in the console again, I saw that :reload-application-on-every-request was now true!
These were all small baby steps, but unless I take them, I won’t learn.
]]>I was at first thinking I might be able to use the new cfhtmltopdf tag, but quickly dropped that idea: I couldn’t get the PDF Service needed to use this tag to work on my Mac; it seems to be an enterprise only feature; and I wasn’t so sure this approach would be compatible to use within Lucee. After looking around over the fence for a bit at potential solutions outside of CFML, nothing hit me as particularly appealing, so I dug into getting cfdocument to work as best I could. First the tag attributes.
1
| |
FontEmbed=“true” is essential so that anyone can print the PDF with the OCR-B code line correctly displayed, even if they don’t have this font on their system. LocalUrl is set to true to easily pull in a logo image from the local file system. Note the margins are set to 0, because I’m using a <div> tag to define the size of the page to control the tendency of the 2 different PDF generation engines used in ACF and Lucee to scale the layout, differently.
Nested directly within the cfdocument tag is a div tag that sets the page width, with position:relative so it remains within the page flow, and acts as the parent tag within which all layout divs that position text are nested and proportionally scaled against:
1
| |
Then within that parent div tag are nested the various div tags, absolutely positioned, containing the blocks of text and images that make up the PDF content. Here are a few examples to demonstrate:
1 2 3 4 5 6 7 8 9 10 11 | |
What I really like about this approach is that each of the text blocks winds up very close to the top and left dimensions specified, and the ACF and Lucee outputs are nearly identical.
Without the parent div tag specifying the width, the results between the 2 engines are vastly different, and positioning the elements is much more a question of trial and error than simply entering the top and left positions as measured with a ruler (and perhaps tweaking them by a few milimeters if necessary). Also without the parent div tag to control how layout elements scale, changing the dimension of one absolutely positioned div within the PDF can easily alter the position or size of other divs, which can be very frustrating if you have 20 or 30 elements that all need to be precisely positioned.
Feel free to experiment with paddings or margins on the parent div if you want - I suspect it would work just as well (but haven’t tried). For myself, I found it easier simply to measure placement from the edge of the page.
I haven’t tried this myself, yet, but I have it from a reliable source that a parent div tag in the layout specifying the page width, as above, will fix page break issues if a table extends over more than one page.
Specifying a font for a block of text is simple. You can use a CSS style declaration within a style block that is nested within the cfdocument tag, or you can place the font specification directly in a style attribute of an html tag like `
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
As of this writing, available CSS attributes remain limited. There is a list of CSS attributes that work in the ACF documentation for cfdocument, reproduced here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | |
Scattered around the internet I found a variety of comments suggesting that getting a “custom” font to work within cfdocument isn’t at all easy. Once you know how, it’s actually fairly simple. For both ACF and Lucee, it’s a question of getting the correct font file in the right place, with the right permissions, and restarting ACF/Lucee.
For ACF, log into the administrator and go to the Font Management Panel. There you will see all the fonts available to use in cfdocument … except that as of this writing, the OTF fonts won’t work, even tho’ the Font Management panel claims they are useable in PDFs. Unless something changes in the future, forget OTF fonts for cfdocument. I tried a bunch that already seemed to be recognized, none worked.
At the top of the font panel, you’ll see a feature that allows you to add fonts “Register New Font(s) with ColdFusion”. I couldn’t get this to work reliably - I think the validation routine on it isn’t well designed. Go ahead and try if you like, but I found it easier to simply find a path to a font directory already recognized by ACF from the list of fonts installed ( I chose a directory with TTF files, just to be sure ), copy the TTF font files you want to use to that directory, change the owner/group and permissions to match the other font files in that directory (if necessary on your OS), restart ACF, log back into the administrator, go to the Font Management panel again and look for your fonts. In the left columns of the font table there are 3 names, Font Family, Font Face, Postscript Name. From my experience, using the font face name in your CSS specification should work. ( If not, try the other names - I saw a post suggesting that. )
To be clear, after you get the font installed in a directory that ACF recognizes as a font directory, one way or another, you’ll need to find the name you need to use in your CSS. The file name often does not match the font name.
For Lucee, the process is different. ;-) Go to your Lucee installation directory, and within the /lib/ directory underneath it, find the fonts.jar file. Copy it to a working directory, rename the copy to fonts.zip and unzip it. ( A .jar file is essentially a ZIP file ). In the unzipped fonts directory that results, add your custom font files, again use only TTFs, and then open the pd4fonts.properties file you find in there and following the pattern you see, add the names of the fonts. In Lucee, you can choose the name, but to be cross compatible with ACF, I used the same name that ACF picked up from the font file. Here’s what my pd4fonts.properties looked like after I added the OCR-B font I needed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
I added the last line, OCRB=ob______.ttf, following the pattern FontFaceName=fontFileName.ttf.
Note that spaces in the font name are escaped with backslashes, so those look like this Font\ Face\ Name=fontFileName.ttf.
Ok, save pd4fonts.properties when you’re done, zip the fonts directory up into fonts.zip, rename it to fonts.jar, copy it back where it came from in the /lib/ directory, overwriting … no wait! Don’t just overwrite the old fonts.jar. Keep a copy of it, just in case … replace the old fonts.jar with the new fonts.jar, change the owner/group permissions to match the old file, and then restart Lucee.
The name to use in your CSS for the font is the name you placed in the pd4fonts.properties file.
Of course, the above approach for Lucee ( 4.5 ) might certainly change in the future. But for now, it works like this.
A big thanks to Michael Hnat for pointing me in the right direction regarding Lucee with his very helpful blog post.
There is a logo image at the top of this PDF that was scaling up when rendered in Lucee in a way that caused it to be misplaced. The top left position was correct, but it was about 50% too big, pixelated, and overran other text. After a bunch of reading that indicated modifying the image print size or resolution would not help, I tried adding a css style declaration to the image that specified a size in mm, and it worked! Thanks to faxi05 for the suggestion.
1
| |
… and it’s cross compatible with ACF!
]]>1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | |
Thanks to Igal Sapir for helping me work this out!
]]>For ease of debugging any problems that may occur, I strongly recommend you always keep a Terminal / Console window open containing a tail of the console output from your CFML server. If you’re using Lucee or a Railo installation based on Tomcat, you’ll want to find the catalina.out log file and tail that. If you’re using ColdFusion 11, even tho’ it is notionally based on Tomcat, it’s completely non-standard and you’ll want to tail cfusion/logs/coldfusion-out.log.
To “tail”, in this case, means to show the newly added contents of a log file in real time. To get this to work, I open Terminal on my Mac and issue the following commands:
… for my local CF11 install:
tail -f /Applications/ColdFusion11/cfusion/logs/coldfusion-out.log
and for my local Lucee install:
tail -f ~/tomcat/logs/catalina.out
You will of course need to change those paths to match the directory under which your applications are installed.
To get out of tail mode and back to the command line, hit CTRL-C.
What I see when I do this is the last 10 lines of the log file, to start with. Anytime an additional line is added to the log file, it immediately appears in the console display in real time.
Now what I can do in my code is send output to these log files. An example should explain it sufficiently. I just used this technique to debug a the following service function, where I wanted to ensure that dateTo UTC was being set as needed:
public query function findRawAudit( string entity, numeric userId, date dateFrom, date dateTo, numeric timeZoneOffset ) {
var stdout = createObject( "java", "java.lang.System" ).out;
...
if ( structKeyExists(arguments, 'dateTo') ) {
sql &= "#newLine#" & "and timestamp < :dateTo";
// adjust dateFrom to account for time zone difference between server and user
var dateToUTC = DateAdd( 'h', -arguments.timeZoneOffset, arguments.dateTo );
// we add a day to dateTo to arrive at an effectiveEndDateTime, namely midnight of the endDate
// so that the endDate is inclusive, as users would very likely expect
dateToUTC = dateAdd( 'd', 1, dateToUTC );
stdout.println( "dateToUTC : #dateToUTC #" );
structInsert(params, "dateTo", { value: dateToUTC, cfsqltype: "cf_sql_timestamp" } );
}
...
}
Note the lines var stdout = createObject( "java", "java.lang.System" ).out; and stdout.println( "dateToUTC : #dateToUTC #" ); which creates an instance of java.lang.System.out and prints a line to the log file.
Immediately upon running this code dateToUTC : {ts '2015-06-16 22:00:00'} appeared in my tail output, which is what I expected. The bug was elsewhere, . I find this very useful. It’s simple and effective, and it works in the same way across ACF, Lucee and Railo. I don’t have to fiddle around with an admin setting to turn on trace output, check if trace output is enabled, remember the syntax used for cftrace / trace, and I can use it directly on a production server in a pinch in case of need.
Of course, you can tail any log file necessary, or use WriteLog() if you prefer. WriteLog() does the exact same thing as stdout.println(), and it’s easier to remember. So taking the above example, I could also use
1
| |
or
1
| |
The first small problem was that when users logged out of an FW/1 app, they would encounter a blank screen rather than being redirected to the login screen. Further investigation, using Chrome’s Developer Tools Network panel, showed that Nginx was returning a blank location response header on the redirect to the login screen. I’m still not exactly clear why this is occuring - other 302 redirects within the app work as expected, but the solution I came up with worked like a charm. All I needed to do was use the proxy_redirect directive alongside the proxy_pass directive, redirecting a blank location to the root location, like so:
1
| |
I was happy that it worked as I guessed it would. In any case, proxy_redirect is a handy directive to have in your toolkit.
In another case, a url pointing to a directory without a trailing slash would result in a 404 error behind Nginx. This url was being processed using the proxy_pass directive. For example, http://domain.com/directory would result in a 404 while http://domain.com/directory/ works, locating the default index.cfm template under /directory.
Since I had only one url to adjust, the solution was rather simple:
1
| |
There are several trailing slash issues using Nginx that need to be taken into account. Search for “nginx trailing slash” to get a handle on them.
]]>Specifically on a Linux or Mac OS, open setenv.sh, which should be located under
1
| |
Restart Railo, and you should be good to go!
The background here is that the default file encoding system used in tomcat isn’t utf-8. There should be Tomcat documentation available for a Windows install somewhere. Feel free to add a comment if you happen to know how to set the file system encoding on Windows.
]]>Here’s the configuration I wanted to test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
First off, I had a doubt regarding how Nginx was selecting which location directive to use for each file requested from the documentation I’ve read. What I wanted to ensure is that Nginx was serving the static files, and proxying only the CFML files.
A deeper issue here is whether or not the proxy_pass directive contains a URI. If it does, you can’t use a regex to filter a location for CFML files only. A few examples will explain this better:
This configuration contains a URI in the proxy_pass directive, namely /domain.com/. You cannot use a regex in the location directive to filter the request, so in my case, the location directive simply had to point to the root ( recursively in the way Nginx works, so essentially this directive will process any file under the root ).
1 2 3 | |
This configuration also contains a URI in the proxy_pass directive, the trailing slash. Again, no regex in the location is possible.
1 2 3 | |
This config does not have a URI component in the proxy_pass directive, no trailing slash and no subdirectory are present here. In this case, we CAN apply a regex filter to pass only those files we want to our application server.
1 2 3 | |
The tradeoff here is that if we use a filter to ensure only CFML files are pass to our app server, then we need to additionally configure each virtual host in Tomcat’s server.xml ( assuming multiple hosts per server ) so that Tomcat can locate the files.
If you’d rather configure each server only in Nginx, as I did with my CF11 install, then another approach might be to use a regex to filter all your static files. Hence:
1 2 3 | |
With the essential background information out of the way, the question that arose was this. Is Nginx actually serving the static files, as I hoped, or are they all being passed to ColdFusion? The documention I found here and there seemed contradictory.
Here’s one way to test this using curl on the command line to fetch the response headers, with the output I got using the above settings:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
See the difference? The image is being cached on the browser and the headers are being set as per the static file location block, while index.cfm isn’t being cached. The expires epoch; directive tells the browser to set the expires date to 01 Jan 1970, a date in the past means don’t cache this, while the expires modified +90d; tells the browser to set the expires date 90 days from the Last-Modified date.
Another way to see these headers is via Developer Tools in Chrome ( or a comparable tool in any other modern browser ). Open a Dev Tools window while browsing the web page you’d like to check, refresh the page, go to the Network tab, and click on any file to see the headers. This approach will give you a better overview of how your Nginx settings are serving and caching each file involved in an http request.
After some thought and study, I decided to change my initial approach to caching static content to one that will help guarentee any content that I modify is served fresh from the server rather that stale from the cache. Here’s what I came up with, for now, for a particular business application that I am still developing:
1 2 3 4 5 6 7 | |
The users who access this site every day will only need to re-download static content once a day, an additional second or two once a day won’t hurt them. In the version of Nginx I’m currently using, enabling gzip disables etags. For now, I’d rather have etag enabled, so I’m disabling gzip for static content, but leaving it enabled for the dynamically generated content that is returned from the proxied app server.
Resources for further reading:
Since I’m in Switzerland, and all my clients use extended characters, in French, German and Italian, I needed a solution. Telling them not to use extended characters in their file names wasn’t palatable. It seemed particularly odd to me that Railo didn’t play well here, since its primary developers are European.
Adjusting the Charset settings panel to ensure UTF-8 was used didn’t help, but I found a simple solution in a bug report that Michel Gallant filed that did the trick for me.
As per Michel comment in the bug report, I added the following line to the end of the tomcat setenv.sh script, which you will find at
1
| |
and then I restarted Railo to pick up the change. I was delighted it worked. Thanks very much Michel!
This server’s OS is Linux CentOS 7, and the above should work for any other Linux install as well. I’m not sure where this might be set on a Windows server, but it is the same file used to adjust the Tomcat/Java memory settings ( -Xms512m -Xmx1024m -XX:MaxPermSize=256m ).
]]>I’ve recently moved a production application from CF9 to CF11. I didn’t catch all the compatibility issues in my testing, so users have been reporting minor bugs and I’ve been fixing them and the uploading them to the CF11 server as necessary. Yesterday, late in the afternoon, I uploaded a template with a very minor change to it and got a cannot find template error. Actually it was a cannot find component error. After repeated attempts to replace the file, and careful double checking (it was in the right place, I could find and read the file via the command line) to try and work around it, I changed another template. Now CF couldn’t find that one either. Huh???
The bigger problem was that it broke the application - it was no longer possible to login. #$%&@!!!!
Nothing I did for the next 6 hours helped. The template was obviously in the right location. It simply didn’t make any sense. I ran through permissions issues, hard disk failure issues, considered cloning the server, wondered how the fact that I had just installed SSL certs might have somehow caused CF to not find a template I had just changed, bizarre as that might seem, thought about updating the JVM or removing the certs from the java keystore, looked at permission issues again, checked and rechecked which user CF was running under, the group it was assigned to, considered whether the server had been hacked somehow, planned how I was going to roll back the entire application to CF9 and shift the data that had changed, thought about switching to Lucee and telling my clients they simply have to bear with me while I work through some compatibility issues. I had to get the server back up by this morning. My clients run their businesses with it. I tried yelling at the server through my monitor. “Bloody well just look, open your eyes, dammit!! It’s right there in front of your nose!!!” Didn’t help.
In the end, I got lucky with my Google foo and found someone else who has run into this issue. Turns out the problem was an option in ColdFusion administrator. The “Save class files” option was checked by default on install. I didn’t change any settings in the Caching section on install, assuming that the defaults would be “safe”, they would not cache anything in a way that would make the server “un-updateable”. I was wrong. The defaults can easily break your application, and leave you no clue at all as to why.
I needed to uncheck it, restart CF, and then the app worked again.
The text next to the checkbox says this:
When you select this option, the class files generated by ColdFusion are saved to disk for reuse after the server restarts. Adobe recommends this for production systems. During development, Adobe recommends that you do not select this option.
It should say:
When you select this option, any changes you make to your files may break your application. The error messages you will get will give you no clue at all what the underlying cause is - namely this checkbox enabling the poorly designed functionality behind it.
I expect options for production systems to make CF more performant, stable or secure. Options that potentially take production applications down with no warning, rhyme or reason are neither expected nor welcome. Given that I now am fully aware of the consequences of checking this box, I would recommend leaving it always unchecked. Otherwise, every time I need to make a minor change to a production server I have to remember to uncheck it, reboot the server, make my change, check it again, and perhaps reboot the server again. How does that improve the experience of my users? And if I somehow forget this arcane procedure, whatever it actually is, and take the app down again with a minor change, perhaps for hours and even days, it’s even worse!
Someone will invariably blame me. I should have known this - and remembered it. Well, maybe this blog post will help both me and perhaps others in this regard. That said, I think the fact that this option causes CF to break production applications is a bug. Feel free to vote for it if you agree.
]]>First up, it was very easy to install, start, and enable so that it always starts at boot time. These are the commands I used for CentOS 7, adapt as necessary for your operating system:
1 2 3 4 | |
I could then browse to the server ip and see the Nginx welcome page.
It then took me a bit of time for me to grok how the configuration setup works. However, compared to Apache’s massive conf file, the fact that I could easily see the entire main configuration in a single screen immediately put me at ease. Basically, Nginx has a main config file, located at /etc/nginx/nginx.conf on my CentOS install, which can be extended with includes. Before I started messing with it, the main conf file looked something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
… and the default site conf file, which is picked up by the include at the bottom of the main conf file above, looked like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
Note the asterik syntax in the include at the bottom of the main conf file:
include /etc/nginx/conf.d/*.conf;.
Effectively, any .conf file you drop in that directory becomes part of the configuration ( specifically of the http{} block ).
Note also that there are semicolons at the end of every setting. If you see errors when reloading conf files using the command nginx -s reload, look for missing semicolons.
In a nutshell, Nginx’s configuration is a simple nested structure, like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |
Each server block defines a virtual server, similar to a virtual host in Apache. The server_name directive takes a space delimited list of domains, which can include an asterisk as a wildcard character, replacing the first or last part of a name, to specify for instance any subdomain or TLD. Note that a directive like server_name example.com *.example.com can be also written with the shorthand expression server_name .example.com
Nginx selects and processes locations within a server block using the most specific match, with regexes taking precedence, followed by the longest matching prefix. So in the above example, Nginx would process a file named myImage.jpg using the second location directive, a file in the downloads directory named myInfo.pdf using the first location directive, a file named index.htm ( or index.cfm ) using the third location directive, and a request to the root of the domain, say to http://domain.com/, with the last location directive, where the addition of the “=” sign specifies an exact match.
See the Nginx Web Server configuration guide for more details.
To use Nginx with either ColdFusion or Lucee, the proxy_pass directive is used, as in the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
In the above example, Nginx passes the request to ColdFusion, running on the same server, via port 8500. It gets the response back from CF, immediately terminates the connection to CF so it is available to handle the next request, and then serves the static files using the settings in the second location directive, along with the rendered html from ColdFusion to the user that made the request. Note that this configuration assumes that you’ve changed the location of the CF webroot.
The first advantage here is that there is no connector needed between ACF or Lucee and the webserver. We don’t need to install a connector as part of the initial server setup, and we avoid any bugs or configuration problems that might crop up with these connectors. We also avoid the necessity to sometimes manually reinstall connectors when updating ACF/Lucee. All that is needed is to add the proxy_pass directive and reload the configuration using nginx -s reload.
The second advantage is that as long as you have a firewall in place that only allows public access via ports 80, ( plus maybe port 443, and whatever port you are using for ssh access), your ACF or Lucee server is secured, simply because without a connector between the webserver and your application server, there is no direct access via port 80 or port 443 to your application server. In our example above, anyone browsing to domain.com/CFIDE/administrator/ will get a 404 error. The CFIDE directory isn’t in the root specified. Port 8500 is closed, so ip:8500/CFIDE/administrator/ won’t return a response. The ACF admin directory is not contained in the default site Nginx root, so ip:/CFIDE/administrator/, for instance, will simply return a 404.
How do you gain access to the admin areas of ColdFusion or Lucee on a remote server if ports 8500 or port 8888 are closed? Simple. Use SSH Tunneling.
In the event that the CFIDE, WEB-INF, or lucee-context directories are exposed through a particular configuration setup in a particular server block, there is a simple way to handle this using location directives that can be set globally or included in each server block necessary, for example:
1 2 | |
or
1 2 | |
or
1 2 3 4 | |
Very few developers spend significant time configuring servers. Hence, it often may happen that the servers we maintain for our clients are not as secure as they could be. The simplicity with which we can secure a ColdFusion server behind Nginx seems a definite advantage.
However, the biggest advantage, to me, seems to be the ease of configuring strong SSL https security on Nginx. Because Nginx uses a reverse proxy setup, Nginx can be configured to handle the https connection with the requestor in isolation. What this implies is that SSL certificates do not need to be imported into the Java keystore. Nor do your SSL certs need to be reimported every time the JVM is updated. Of course, if you require a very secure setup, in a load balanced configuration to multiple application servers, you might need to ensure an SSL connection between Nginx and ColdFusion / Lucee. But if, for instance, Nginx and ColdFusion / Lucee are located on the same server, or you trust the internal network, this would not be necessary.
Following this excellent guide by Remy van Elst, I was able to relatively easily setup SSL for a web application to an A+ level as graded by Qualys SSL Labs. Here’s what the SSL portion of my Nginx configuration looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Note that I’ve reduced the max-age value of the add_header Strict-Transport-Security directive to four days from what Remy has recommended, over uncertainty how this will play out when certificates are updated. I understand that this directive will force user agents to use https for the duration of the value set. Note that it will be updated into the future every time the header is sent to the browser.
There are 3 aspects of this configuration that require a bit of extra work on your part, besides simply copying, pasting and adjusting the above example. One is that if your SSL certificate provider has also issued intermediate and root certificates, you will need to concatenate these into one file. Your domain certificate must come first, followed by the intermediate certificates, and finally the root certificate at the end. The easiest way to do this is to create a blank text file, and copy and paste the contents of the certificates into it. Save it and upload it to the chosen location on your server, along with the private key. Point to the location of the concatenated cert in the Nginx config file using the ssl_certificate directive. Point to the location of the private key using the ssl_certificate_key directive.
On Linux, you should secure the key by running chmod 600 on the ssl directory that contains the certificate files and chmod 400 on the private key file itself.
To generate the file referred to in the ssl_dhparam directive in the above example, it is necessary to cd to your ssl certs directory and run the following command, as explained in Remy’s guide in the Forward Secrecy & Diffie Hellman Ephemeral Parameters section:
1 2 | |
You will be warned that “this will take awhile”. Remy doesn’t mention this, but be prepared for it to take a relatively long time, perhaps an hour or so.
Once you have ssl configured and working, you probably want to redirect any traffic trying to access the site via insecure http to secure https. Here’s how to configure that:
1 2 3 4 5 | |
I already mentioned what I think is the biggest advantage, the ease with which one can configure SSL. However, there is one other advantage I’d like to mention that may trump that, the ability to easily configure Nginx for both load balancing and hot swapping of application servers. Here’s how to do that using the upstream directive:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Then instead of using the localhost IP address in your proxy_pass directive, use the upstream server name instead, like so:
1 2 3 | |
If you get caught in a bind and can’t use ColdFusion for some reason, then hot swapping an application over to Lucee is as simply as changing the above to this:
1 2 3 | |
Or if you find one of your web properties suddenly inundated with traffic, using the upstream directive, your webserver is already set up for load balancing. You’d simply need to clone a few servers, boot them up, and add them to the upsteam directive. The ip_hash directive is a way to enable sticky sessions, to ensure a visitor is always routed to the same server.
A few more configuration options and details should be mentioned.
One is that it is also possible, and perhaps preferable, to configure the path to a website’s files within Tomcat’s server.xml file, rather than specifying it in the Nginx proxy_pass directive.
1 2 3 4 | |
With the path to the website’s files configured in server.xml, the path to the website’s files is not appended to the proxy_pass directive, so it would look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
For this to work, Nginx needs to pass the host name to the app server used, either Lucee or ColdFusion. Your application may also require this information. Here is a suggested set of directives to accomplish this:
1 2 3 4 5 6 7 8 9 | |
… which could be placed in a separate conf file and included with each proxy_pass directive, like so:
1 2 3 4 | |
If you are using Lucee, adding a host configuration block to server.xml will cause Lucee to create a WEB-INF directory under the docBase path specified along with an additional context. You’d then want to block public access to that directory in some way as I’ve explained above.
You can also use a regex to ensure you only pass CFML templates to Lucee or ColdFusion, as the following example demonstrates:
1 2 3 4 | |
You can put whatever file extensions you’d like Lucee / ACF to process in the pipe delimited list within the regex, so ~ \.(cfm|cfml|cfc)$ or ~ \.(cfm|cfml|htm|html)$ could be used, for example.
A couple of small gotcha’s I ran into setting the path to the website’s files in the proxy_pass directive. One is that directories with dots in them can confuse mappings. The solution I used was to remove the dots from directory names and use hyphens instead ( my-domain-com ). I also had trouble with redirects in FW/1, since they rely on the value of cgi.script_name. The simple solution was to change the baseURL parameter of FW/1’s config to baseURL = '' from the default baseURL = 'useCgiScriptName'. Problems such as these might be avoided by using the server.xml configuration option.
I have 2 production servers running Nginx at the moment, one ACF and one Lucee server, and so far it seems to be working very well. I’m particularly happy with the choice because Nginx has a very small memory footprint, which leaves more memory available for the JVM, which indeed yet another advantage.
References:
]]>The first step is to locate the server.xml file, which you will find at
Make a backup of it. If something goes wrong, you can use the backup to restore CF to a working state again. Note that CF needs to be able to locate the CFIDE and WEB-INF directories to function properly, and these are both located with in the default webroot location. So make a backup of server.xml before proceeding.
Open server.xml for editing, scroll down to the bottom of the file where you should find the following line commented out:
1
| |
Assuming the installation directory is /opt/cf11, so that I can give a concrete example, uncomment that line and adapt it to the following model, which is working on my CF11 installation:
1
| |
The docBase is the new webroot location, which must already exist, but can be of your choice. WorkDir points to an existing location in the installation directories. The aliases are essential, so that CF can find the CFIDE and WEB-INF directories. Adjust the slant of the slashes depending on your operating system, Windows or Linux. Use absolute paths for these settings, so on a Windows server, they would likely begin with a drive letter.
After you’ve edited and saved server.xml, restart ColdFusion, place some cf code in the docBase directory, and browse to it via localhost:8500/ to make sure it works. Also check if you can still access the CF admin panel at localhost:8500/CFIDE/administrator/, which CF should find via the aliases= declaration. If both those tests succeed, you should be good to go!
References:
]]>The first thing to understand about firewalld is that it is has multiple layers. It comes with a predefined set of zones, namely block, dmz, drop, external, home, internal, public, trusted, and work. Each of those zones can be associated with a network device or one or more ip addresses. Essentially, zones define and demarcate the level of trust an admin has decided to place on the devices and traffic within a network.
firewalld also pre-defines a set of services that can be added or removed from a zone. Effectively, when a service is added to a zone, it opens a port and sets any other necessary parameters. Services are defined with XML. Here’s what the http service looks like:
1 2 3 4 5 6 | |
So to open port 80 via the tcp protocol to serve http requests, as an example, first a zone must be associated with the network device that will handle the traffic, and then the http service added to the zone. As an admin, you can define your own custom services, or customize existing services. Other techniques allow you to open a port directly on a zone, or define more complex rules for access.
To configure the firewall and check its status, a command line client is provided, firewall-cmd. It can be used to make both permanent and temporary config changes. The configuration for firewalld is stored in XML files in /usr/lib/firewalld/ ( the default settings, not to be modified! ) and /etc/firewalld/ ( for user configured settings, which have preference over those in the default location ). These files can be edited, backed up, or used as templates for other server installations.
Now that we have an overview, we can get to work. To check if firewalld is running:
1
| |
If you see from the output that firewalld is not running, or you see that the loaded service is disabled, here are the commands needed:
1 2 | |
If a service is enabled, it will start on system reboot. Hence it’s particularly important to ensure firewalld is enabled on a production server.
Here’s how to disable firewalld so it will not start at boot time, and shut it down:
1 2 | |
Now I want to configure the firewall. First, I check for the name of the ethernet interface so that I can refer to it to associate it with a zone:
1
| |
Then I check which zone eno16777736 is currently assigned to:
1
| |
The result is “no zone”, so the next step is to add the ethernet interface to the public zone, which is the zone I’ve decided to use for http access to the server. It’s important to add the –permanent flag to the command so it is retained permanently, across reboots:
1
| |
Now I have to reload the firewall configuration for the changes to take effect:
1
| |
And then we can double check just to make sure the ethernet interface is now added to the public zone …
1
| |
and the result is “public”, so that’s now set up correctly.
Let’s now check how the public zone is currently set up:
1
| |
Here we see again that the ethernet interface is added to the public zone, and that it is both active and the default zone. By default after installing CentOS 7, we have the services dhcpv6-client and ssh added to this zone. Taking a quick look at the description for this service to see what it does by opening /usr/lib/firewalld/services/dhcpv6-client.xml, we see, “This option allows a DHCP for IPv6 (DHCPv6) client to obtain addresses and other IPv6 settings from DHCPv6 server.” We won’t be using IPv6 addresses within our local network to access this machine, so I think it’s safe to remove this service, although we may want to leave it in place on a production server:
1
| |
Reminder - remember to always add the permanent flag to these commands if you want changes to be persisted!
Now we can add the services for http access to our public zone:
1 2 | |
… reload the firewall …
1
| |
… and recheck the configuration, using list-services instead of list-all just to try it out:
1
| |
and I see that we now have services http https ssh configured. Excellent. Let’s test that in a web browser.
I’ve installed nginx web server, but see using systemctl status nginx that it’s not yet running or enabled, so first we run
1 2 | |
And then I go to 192.168.1.16 in a web browser and see Welcome to nginx! Good.
As a double check, let’s remove the http service and see what happens.
1 2 | |
Reloading 192.168.1.16, I get a No data received message, so that’s exactly what we should expect.
1 2 | |
And adding the http service back to the public zone again allows the Welcome to nginx! page to be loaded in my browser. Perfect.
However, I still don’t have access to the CF Admin panel at http://192.168.1.16:8500/CFIDE/administrator/index.cfm, because that’s over port 8500. On a production machine, I absolutely would not open port 8500 for this purpose. But since this server is on our local office network, let’s see how we can do this.
The first option that comes to mind is to create a custom firewalld service specifically for this purpose. Documentation I’ve read recommends using existing services as a template. Custom services go in /etc/firewalld/services/. First let’s make a copy of the http service, calling it http8500, and place it in /etc/firewalld/services/:
1
| |
Then we edit /etc/firewalld/services/http8500.xml to use port 8500 instead of port 80. Here’s what the modified file looks like:
1 2 3 4 5 6 | |
Then we add this service to the public zone and reload the firewall:
1 2 | |
And now http://192.168.1.16:8500/CFIDE/administrator/index.cfm works! Again, this is not how I’d set up access to the CF administrator on a production machine, but it was an opportunity to experiment with creating custom services. What I like about this option is that I can enable or disable it, independently of the other services enabled. So if I decide I want to lock this server down, I can quickly remove the http8500 service and access the CF Administrator via SSH Tunnelling.
What I usually do is move ssh access to an obscure port. I think we can easily accomplish this using a custom service, but before I do that, I want to take a look at how the localhost interface is set up within the firewall. Again, we use nmcli dev status to get the name of the localhost or loopback interface
1
| |
It’s “lo”, so let’s see if it’s set to zone by default:
1
| |
Nope, the result I get is “no zone”. Let’s also see if there are any services added to the trusted zone, which would be the most appropriate for localhost
1
| |
At this point, nothing is added to this zone, no interfaces, services, sources or ports, etc. And the network interface “lo” isn’t associated with any zone.
Now what I want to see is how the server responds to localhost access with the firewall enabled. This might be important on a production server because I will use ssh tunneling to access any areas I will restrict from public access. So let’s logout from the server and login again with the -D flag so that I can tunnel into the test server and test if I have access via localhost with the firewall setup as it is now:
1 2 3 | |
I keep Firefox on my dev machine reserved and set up for ssh tunneling on port 6100, so I simply open Firefox and browse to http://localhost:8500/CFIDE/administrator/index.cfm, and find I can access the CF11 admin page and login. Browsing to http://localhost/, I see the Welcome to nginx! page. So at this point via localhost, I have access. ( Note for anyone without experience using ssh tunnelling, when I use localhost on Firefox set up for ssh tunneling, logged to the CentOS server using the -D flag, I am browsing the CentOS server next to me, not my dev machine. See SSH Tunnelling for details how to do this. )
Now what happens if I add the “lo” network interface to the trusted zone, where no access is currently set up?
1 2 | |
Adding the “lo” interface to the trusted zone with no services had no affect at all on tunnelled access to localhost. So it looks like the firewall doesn’t interfere there. So to clean up, I will remove the “lo” interface from the trusted zone and call it a day.
1 2 | |
PS - For some reason, “lo” was not removed from the trusted zone according to firewall-cmd –zone=trusted –list-all unless and until I rebooted the server. The strange thing was that the config file was correctly altered, but somehow, firewalld didn’t seem to pick up the change. Perhaps this is the intended behavior, to prevent lockout during a current session, but I’ll look into filing a bug report later this evening … ( which I have now filed here ).
References:
]]>For ease of access, each VM needs a static ip on the local network. It’s not hard to do once you figure it out, but getting all the pieces in place took some hours of digging, trial and error. This post, while specific to VMWare and CentOS 7, is intended to help both my future self and anyone else set up networking quickly and easily in such a scenario. Adapt as necessary for your specific environment.
The obvious first step was to download the ISO image from a CentOS mirror site. I grabbed the minimal install as a nearest approximation to a production environment, and created a VM from it using VMWare on an unused Mac mini we had laying around the office.
On the minimal install, networking isn’t enabled by default. So before I could proceed, I had to figure how to enable networking, and get it working via a static IP. Here’s a summary of what finally worked for me.
1) Figure out what the ethernet device is named by running the command :
1
| |

As you can see, mine was named eno16777736, which is not the RedHat default you may find in many examples online.
2) cd to the directory /etc/sysconfig/network-scripts and run ls to display its files
1 2 | |

3) Look for the configuration file for your ethernet device, mine was named ifcfg-eno16777736 and open it for editing using vm or nano
1
| |

The above screenshot was taken after it was edited. The lines to change or add are:
1 2 3 4 5 6 | |
I found that adding the correct gateway ip was essential. NM_CONTROLLED specifies whether or not this device is controlled by the Network Manager. We are setting the parameters manually here, so this must be no. ONBOOT=yes specifies to connect this network device on boot.
Save the file, exit nano, and run the following command to restart the network:
1
| |
Now CentOS 7 should be setup to network via the static local ip of your choice. But the connection isn’t bridged outside of the VM. After some fiddling around, here’s what worked for me. I went out to the VMWare interface, clicked on the double arrow icon to open the networking menu, and clicked Network Adapter Settings…

From the menu, I chose Autodetect, as shown below:

Once I had these configuration changes in place, I could access the CentOS instance via SSH and SFTP from my dev machine.
By the way, from my reading, it also seems possible to use the Network Manager to achieve the same end. In this case, you’d leave the config file for your network device alone, and instead run the nmtui command. Search Google for more complete instructions. You’ll still need to bridge the connection through VMWare tho’.
Hope this helps somebody.
PS - If you can access the instance via SSH or SFTP, but cannot from a browser, you may need to either disable and stop firewalld:
1 2 | |
or better, figure out how to configure it properly to allow access via a browser, which I cover in the next article, Exploring CentOS 7 firewalld
]]>1 2 3 4 | |
or this on Lucee:
1 2 3 4 | |
So far so good, but now anyone needing to administer the server can’t login. They will be denied access by the web server. There are various recommendations for allowing only selective access, for instance from a given fixed IP address at the office of the administrator:
1 2 3 4 5 | |
But what if the server administrator(s) is/are out of the office when they need access to the server? Away at a conference, at a client’s office, or at home? And what if your office doesn’t have a fixed IP address? Should you get one … and then have to remember to change the “allow from” IP address in all of your server configuration files if and when that IP address changes? For a variety of reasons, this doesn’t seem ideal.
There’s another “small” problem here. Best security practice dictates that all access to admin areas should be over an encrypted connection. I cannot ensure that the modem installed here in my office, or wherever else I happen to be when needing to admin my servers, a hotel for instance, is not compromised by a packet sniffer. In fact, I recently installed an update on our modem here specifically for a packet sniffing vulnerability, and I only ran across it by chance. No idea how long that remained unpatched, and I’m simply not prepared to invest the time to learn how to monitor this sort of thing. This implies that I definitely should install SSL certificates and set up secure https access for all administrative areas on each server I maintain. That’s painful.
Put all that together, and it helps to explain why ACF and Lucee servers are often not locked down.
Here’s where SSH tunneling comes to the rescue. In a nutshell, it allows you to securely browse a remote server as localhost, as if the server was under your desk and your keyboard and monitor where connected to it, even if it’s located halfway around the world. The icing on the cake is it is very simple to set up, and the same setup will work for every server you admin!
To use SSH tunneling to gain access to admin areas of your server, the first step to restrict access to any admin area to only localhost, for example:
For ACF:
1 2 3 4 5 | |
or for Lucee:
1 2 3 4 5 | |
( While you are at it, restrict port access to only those ports you need to leave exposed, for instance 80, 443 and whatever port you are using for SSH access ).
There are detailed instructions for SSH tunneling all over the web if you want to find out more, but the instructions below should work fine for our purposes.
In your web browser, configure the proxy settings to point to “localhost”, a free port on your local machine (we’ll use 60001), using SOCKS5. This should work with any browser. Using Firefox as an example, here’s how to do that:
ssh into any server you admin using Terminal or Putty, etc. Use the -D flag set to the same port as above, example: ssh -D 60001 user@102.103.108.39
You can now access admin areas of the server in this browser using localhost urls such as http://127.0.0.1/CFIDE/administrator/enter.cfm, as long as you remain logged in via SSH. The connection is through an SSH “tunnel”, so between your local machine and the server, all traffic is encypted.
Note that the port chosen is arbitrary. It only has to be available and match in both the -D flag and SOCKS port setting. To revert the browser to normal behavior, simply choose No Proxy in the Network Settings dialog.
What I usually do is leave Firefox configured in this way and reserve it only for SSH tunnelling sessions. And again, once a browser is configured with these proxy settings, you can securely browse any server as localhost by SSH’ing into it with the -D port setting.
Now say you have Fusion Reactor installed and want to ensure access is also restricted. Just leave your firewall configured to leave the ports Fusion Reactor uses closed, and access it, securely, via your SSH tunnel!
Thanks to David Stockton, who until recently worked with the fusion reactor team, for sharing this tip with me.
IMPORTANT NOTE: If you have difficulty logging in, or maintaining a login, particularly to the ACF admin panel, clear the cookies in the browser you are using for SSH tunneling and try again.
In my case, I ran into this after using this technique to manage multiple instance of ACF, particularly on ACF 11. I could log in to the admin panel successfully, but if I tried to modify anything, set a datasource for instance, I was logged out and the action did not complete. The error log contained the message “There was an error while verifying the token. Either the session timed out or un-authenticated access is suspected.”
]]>1
| |
and ensure the following line is included in the config settings
1
| |
2) Open the rpm macro file ( or create it if it doesn’t exist ):
1
| |
And ensure the following line is included:
1
| |
Now the rollback flag can be used on the rpm command as shown in the examples below to roll back updates to any point in the past:
1 2 3 4 | |
Simple.
]]>I run several instances of ACF on CentOS. I’m subscribed to the CentOS-announce Digest mailing list, as I’m sure most CentOS server admins are. For many years now, whenever an update is released, perhaps once or twice a week, I ssh directly onto our servers, run yum update, and generally within less than a minute, the update completes. Never once has the process failed. Never once have I seen an incoming email on the CentOS mailing list that someone experienced a server crash, or any other issue for that matter, because of an update.
This is how ColdFusion updates should function, flawlessly.
In stark contrast, CF developers have become wary of applying updates. There is a risk that their server will compromised because of the security vulnerability the update is meant to patch, and there seems to be an equal if not greater risk that their server will be compromised, in one way or another, in the update process. CF 10 introduced automated updates, but as we see, the automated update process remains error prone.
It should not be like this. Rather than focusing on new features, the CF dev team needs to focus on getting the update process as flawless as it is on CentOS. What they seem to have lost track of is that, for a start, developers have families to support. Our clients expect us to keep their servers up and running 24/7. Our clients depend on the applications we build to run their businesses. As developers, we cannot afford to experiment on our clients. And yet, when Adobe releases a flawed update that takes servers down, effectively experimenting on us and our clients, oh, look, they are trapped between a security vulerability and a flawed update, the implicit statement received on our end is “We don’t care if you keep your clients. It does not matter to us at all.” Really?
What is even more disturbing is the way the Adobe dev team now uses us as their QA department, as if ColdFusion server was loosely organized open source project among wannabe hackers. It is not disturbing because the CF devs are actively reaching out trying to solve problems that are occurring, asking for logs, error message screenshots, suggesting the possible need to copy and paste jar files from here to there if some functionality is broken, instructing us to manually install the updates one at a time if the automated updater doesn’t work, etc. It is because these interchanges should never occur in the first place regarding an update. The update process needs to be flawless, as near to it as possible, so that we can rely on it. Like it is on CentOS. If they can do it, the CF dev team can do it.
The worst, however, is this, linked for reference but replicated below, an Adobe engineer asking a developer, a paying customer, to effectively crash his server “in off peak hours” so that he can get at the log files that might reveal what the problem is.
Hi Mark,
Of all the things connector logs with debug_level=debug are the most important one’s. Till now with all the information you have shared I can tell that there is some problem with shared memory access between worker threads. But to exactly pin point the problem I will be needing the connector logs. I understand it will be difficult for you to do something on production server but it is a must for this problem to be resolved.
If possible in off-peak hours, apply the update14 and reconfigure the connector. Once the connector is installed go to connector directory c:\coldfusion10\config\wsconfig\magicnumber\ and open isapi_redirect.properties in notepad.
Edit the line “log_level= info” and change it to “log_level= debug” and save the changes. Restart IIS, hit any cfm page and wait for app pool to crash. Once it is crashed take a backup of isapi_redirect.log(created in connector directory) in some other directory like c:\connector_logs\ directory. After the backup you can revert back the changes.
Lastly you have to share this backed up logs with us.Thanks,
Milan.
When I say this is unacceptable, I mean that it is not acceptable for Adobe to use CF developers and their clients as their QA team, especially in that Milan says “I understand it will be difficult for you to do something on production server but it is a must for this problem to be resolved.” No, it is not a must to take down our servers, possibly endangering our client relationships, to fix your bugs.
Yes, this is very complex stuff. But in today’s world, that simply means Adobe needs to employ a vastly improved QA strategy than the one they have in place today for ColdFusion releases, especially for the update process itself.
]]>arrayLen( ccnumber )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
I was hoping I could quickly adapt the above for my use case. However, I was disappointed to learn that Swiss pay slips use a non-standard algorithm to calculculate check digits. And genuinely disheartened when I was left staring at this explanation of the algorithm, the only one that seems to be provided by the banking sector.

If you study it for a few minutes, you’ll see how the check digit is calculated, using the chart provided. But it doesn’t give much of a clue what algorithm should be used to generate the check digit, except in the title, which seems to suggest it might use mod 10, recursively, if you assume that it has been translated from German incorrectly, module instead of modulo or modulus.
After a few sessions of Googling, I found an example someone wrote in Delphi that seemed to demonstate it could be tackled rather simply, with a single array representing the entire chart. Unfortunately I couldn’t grok some of the nuances of the syntax.
Then I ran across another set of examples variations of C and Visual Basic. His comment at the top of the page reads:
Haben Sie sich auch schon über die nicht sehr programmiergerechte Beschreibung in den Unterlagen der PostFinance und der Telekurs geärgert? … which my Swiss client on this project translated as “The descriptions from PostFinance and Telekurs are shit…” - not at all useful for programmers.
The VB example looked familiar enough, and after a bit of mucking around, came up with this CFML function that seems to work to generate the check digit, passing every test I’ve thown at it from the pay slips I have laying around.
1 2 3 4 5 6 7 8 9 10 11 12 | |
If you compare the VB solution with the above, you’ll see I’m adding 1 to each calculated index value. That’s because ColdFusion arrays are 1-based rather than 0-based. If you happen to be using this example to build a similar function in another language that has 0 based arrays, remove the + 1 from line 7.
This function expects a string with consecutive numeric digits 0-9 only. It will blow up if any other characters are passed into it.
Here are some reference links for solving the more general problem of generating Swiss payment slips:
Manual ISR - Orange Inpayment Slip with reference number - the clearest explanation I’ve found regarding how the data is structured and placed on the pay slip.
Record Description Electronic services - the only information currently here is reproduced in the logic diagram above.
]]>