Tuesday, 30 July 2013

CFML: Reload Coldspring on each request when in a dev environment

G'day:
This is based on another question Rob asked about Coldspring a few days back:

Is there a way to make #Coldspring reload the CFCs on each requestion, for dev purposes? #coldfusion

I've had the same question myself, but never had to act on it so never bothered to check.

At the time I responded that I was pretty sure there was no "mode" in Coldspring to get it to change its load behaviour, and indeed one would usually only load beans when the application starts anyhow, so by the time a subsequent request comes around, everything's loaded.

Some people have code in their onRequestStart() which looks for a "reload" (or similar) URL param, and does an app-restart if it's found. This is an OK approach, but for some reason it's never sat that well with me. My feeling is that one's environment doesn't change from request to request, so it shouldn't be a request-centric process. It also leaves functionality intended for the dev environment accessible in the live environment, which isn't ideal IMO. It's not a major concern and is easily-enough worked around, but it got me thinking about alternative approaches. Not necessarily better ones, just an alternative.

Here's an approach which is based on the environment the code is running in, with the environment settings being dictated by an environment-specific config file.

In source control there's a bunch of environment config files, eg:

/config/
    environment-config-DEV.json
    environment-config-LIVE.json

Update:
As per a couple of the comments, I had been slightly too abstract for my own good in the code example, so I have modified it a bit. The intent was not to suggest one would always want to run Coldspring in a dev environment, just that if one wanted to whilst one was working on something that needed reloading every request, then it can be achieved using an environment-specific configuration. Hopefully my tweaking of the code has made this more clear. Or less unclear.

And when deploying, the deployment script takes the file appropriate for the deployment environment and deploys it as environment-config.json. In this case, a very pared down config file might be:

{
    "coldspringLoadMode"    : "REQUEST"
}


From there, the Application.cfc file reads environment-config.json (which is now environment-specific), and acts accordingly. EG:

// Application.cfc
component {

    this.name = "coldspringTest01";
    this.mappings = {
        "/coldspring" = expandPath("../coldspring"),
        "/api" = expandPath("../api")
    };

    public void function onApplicationStart(){

        application.environmentConfig = deserializeJson(fileread(expandPath("../conf/environment-config.json")));

        if (application.environmentConfig.coldspringLoadMode == "APPLICATION"){
            initColdspring();
        }

        writeOutput("Application initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
    }

    public void function onRequestStart(){
        if (application.environmentConfig.coldspringLoadMode == "REQUEST"){
            initColdspring();
        }
    }


    private void function initColdspring(){
        var properties = initColdspringProperties();

        application.beanFactory = createObject("coldspring.beans.DefaultXmlBeanFactory").init(defaultProperties=properties);
        application.beanFactory.loadBeansFromXmlFile("../conf/beans.xml", true);
        writeOutput("Coldspring initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
    }

    private struct function initColdspringProperties(){
        return {timestamp=now()};

    }

}

So Application.cfc doesn't need to have logic to decide whether it's in LIVE or DEV mode, it just loads the config file, and then uses the environment-specific settings.

From a Coldspring perspective, we then have conditional logic as to where we do its initialisation. To facilitate this we factor out the Coldspring initialisation into its own method, and then conditionally call that method onApplicationStart() (when "live"),or onRequestStart() (if "dev").

NB: the Coldspring / bean config itself is the same as in the article I knocked-out this morning.

As a test rig, I have this code:

// test.cfm
writeOutput("test.cfm initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
o1 = application.beanFactory.getBean("C");
writeOutput("o1.timestamp = #timeFormat(o1.timestamp, "HH:MM:SS.LLL")#<br>");

Which just demonstrates when o1 was initialised. On dev, the output from the first two requests look like this:

First request:
Application initialised @ 13:39:33.257
Coldspring initialised @ 13:39:33.275
test.cfm initialised @ 13:39:33.275
o1.timestamp = 13:39:33.258

Second request:
Coldspring initialised @ 13:40:31.352
test.cfm initialised @ 13:40:31.353
o1.timestamp = 13:40:31.345

So Coldspring is being init-ed  each request, and the C instance that gets assigned to o1 is - accordingly - too.

In the live environment, Coldspring is only loaded when the application starts, so o1's C instance is only created once, too:

First request:
Coldspring initialised @ 13:42:41.695
Application initialised @ 13:42:41.695
test.cfm initialised @ 13:42:41.696
o1.timestamp = 13:42:41.686

Second request:
test.cfm initialised @ 13:43:05.785
o1.timestamp = 13:42:41.686

That's it, really. I'm not saying this is a better approach to having URL-based stuff, but it's a case of different horses for different courses. Each approach has its pros and cons. And, indeed, both can be used in conjunction with each other.

I've got a meeting to go to...

--
Adam

Installing Coldspring 1.2

G'day:
This article is partly a piss-take, and partly actual guidance. Mostly the latter. Thanks to Rob Glover (again!) for the inspiration for the article (both the piss-take and the actual meat of the article).

Whilst on the train this morning, I read this Twitter update from Rob:

Amazing. NOWHERE can I find INSTALLATION INSTRUCTIONS for #coldspring. Just lots on how it works. #coldfusion
That is quite amazing, but not in the way Rob means. Because a quick google of "coldspring install" returns a bunch of results, and just browsing through the first few:

  1. ColdSpring - Home
    Well... yeah, OK: this is a fail. I've done superficial clicking around, and didn't see any installation instructions within the first ten clicks of hitting the home page.
  2. [PDF] Reference Guide - ColdSpring
    The third item is the installation instructions.
  3. How to install the ColdSpring framework - Hosting Forum - Hostek ...
    The raison d'ĂȘtre of the document is that its instructions of how to install Coldspring.
  4. Mach II and Coldspring for n00bs | Ollie Jones, software hints and tips
    Includes instructions for installing Coldspring
  5. ColdSpring - Railo
    Is not actually about installing Coldspring.
  6. A Beginner's Guide to the ColdSpring Framework for ColdFusion ...
    Unfortunately - given the title - doesn't actually include how to install it.
From here, the docs are referring to Coldspring installation / config issues, or tangential issues people have had, so not really relevant. Still: in the first six Google results, five are about Coldspring installation, and three of them have instructions.

So what's amazing about this is that Rob couldn't find any of this stuff.



But just for the sake of completeness, here are some instructions for installing Coldspring.

Update:
As Mark Mandel has pointed out to me, I only cover 1.2 here, and there is also 2.0 in the works (the status of it is TBC... I don't actually know). I'll find out if 2.0 works differently, and write a companion article if required.
  1. Download Coldspring from the Coldspring website. At time of writing, the current stable version proclaims to be 1.2, and there's an alpha of 2.0 available too. There's a download link for 1.2 (zip) on the homepage.
  2. Unzip the file into a non web browsable directory. Do not put it in your web root (this conflicts with most of the instructions you will see). After unzipping, you should have this sort of directory structure:

    /[parent directory]/
        /[coldspring directory]/ (can be called anything, but "coldspring" makes sense)
            /beans/
                    DefaultXmlBeanFactory.cfc
    
    There will be a lot of other files in there, but that gives you an idea of what the directory structure is like.
  3. In you application's Application.cfc, create a ColdFusion mapping to the [coldspring directory], eg:
    this.mappings = {
        "/coldspring" = expandPath("../coldspring")
    };
    In this case, my web root is adjacent to the coldspring dir, eg:
    /[parent directory]/
        /coldspring/
        /webroot/
            Application.cfc
    
  4. as an alternative to creating the mapping in Application.cfc, one could instead create it in CFAdmin, but I think doing it in the application is preferable.
That is it as far as installation goes.

Note that a lot Coldspring installation instructions will by default suggest just dumping the Coldspring directory in the web root, because this is "easier" because it doesn't require the mapping. This is really poor advice for a few reasons:
  • It's not exactly difficult to create the mapping.
  • The Coldspring files are not intended to be web browsable, so why would the instructions suggest putting them in the web root? That's just wrong.
  • Having files not intended to be web browsed in a web-browsable directory is a potential vector for site hacking. 
In fact most framework installation instructions prefer the lazy / wrong / insecure installation approach to the equally-easy / good / secure approach. So when you read framework installation instructions which advocate putting the framework in the web root as a default practice, screw your nose up in disgust, and don't do that. Generally a framework will be happy to be installed anywhere, and all it needs is a CF mapping with a known name (eg: /coldspring in this case).



So the install for Coldspring is easy. Here's a sample bare-bones application building on that, showing some of the config.

// Application.cfc
component {

    this.name = "coldspringTest12";
    this.mappings = {
        "/coldspring" = expandPath("../coldspring"),
        "/api" = expandPath("../api")
    };

    public void function onApplicationStart(){
        var timestamp = now();

        writeOutput("application initialised @ #timeFormat(timestamp, "HH:MM:SS.LLL")#<br>");

        application.beanFactory = createObject("coldspring.beans.DefaultXmlBeanFactory").init(defaultProperties = {timestamp=timestamp});
        application.beanFactory.loadBeansFromXmlFile("../conf/beans.xml", true);
    }

}

Here we have our /coldpsring mapping, and the bootstrap code to get Coldspring loaded, and my beans loaded too.


I'm passing a bean config parameter too.

Here's my bean definition XML file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="C" class="api.C">
        <constructor-arg name="timestamp">
            <value>${TIMESTAMP}</value>
        </constructor-arg>
    </bean>
</beans>

Note that the /config directory is also not in the web root. It's adjacent to the /coldspring and /webroot and (see below) /api directories.

And I obviously have this C.cfc file too:

// C.cfc
component {

    public C function init(required date timestamp){
        this.timestamp = arguments.timestamp;

        writeOutput("C initialised @ #timeFormat(timestamp, "HH:MM:SS.LLL")#<br>");

        return this;
    }

}

Finally, I have a file which uses Coldspring to fetch my bean:

// test.cfm
writeOutput("test.cfm initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
o1 = application.beanFactory.getBean("C");
writeOutput("o1.timestamp = #timeFormat(o1.timestamp, "HH:MM:SS.LLL")#<br>");
sleep(5000);

writeOutput("current time #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
o2 = application.beanFactory.getBean("C");
writeOutput("o2.timestamp = #timeFormat(o2.timestamp, "HH:MM:SS.LLL")#<br>");

And the output from this is as follows:

application initialised @ 08:38:21.029
test.cfm initialised @ 08:38:21.053
o1.timestamp = 08:38:21.029
current time 08:38:26.066
o2.timestamp = 08:38:21.029

What the timestamps show is that the C bean is created once, during application start-up, and then calls to getBean() simply return that bean. Note that I pause for 5sec in test.cfm, but the timestamp o2 has is the original timestamp from app start-up, not the current timestamp from when o2 was created.


There's obviously a bunch more config options for Coldspring, but this is not an attempt to replicate the docs, it's just a top level summary of how to install it, and a bare bones example app showing the minimum config.

Hopefully this will help people trying to find Coldspring installation docs online.

I've got another article to come on Coldspring: also based on a question Rob asked the other day. Stay-tuned.

--
Adam

Monday, 29 July 2013

Javascript: explicit arguments or config object? When to use which?

G'day:
Just to go off on a tangent... I'm doing Javascript this week, so the puzzling stuff I encounter will probably be JS rather than CFML.

Whilst I know a decent amount about CFML, my JS is... limited. I know enough to get into trouble... I know enough to know when I'm in trouble... but not necessarily enough to get me back out of trouble again.

Survey: "ColdFusion Migration issues: Java 6 to Java 7"

G'day:
I've decided I want to know more about any CFML code incompatibilities / issues that arise from migrating the underlying JVM from Java 6 to Java 7. I've created a quick - single question, written answer - survey to gather info from people. Here's the intro text from it:

Sunday, 28 July 2013

OK, I'm very confused: arraySort() with a callback...?

G'day
I'm sitting at Shannon Airport awaiting my return flight to London, and killing time via Guinness and responding to comments on my blog. There are no little old catholic ladies here today to keep me bemused, indeed it's very quiet.

Anyway, this is the comment I'm currently trying to reply to:

I executed the example you have showed above for customised sorting using arraysort function, but i didn't get the same result. The output i got was the array that i defined in the code. I can't understand why its not happening for me.
 The code in question is this:


a = [
    {firstName="Witi", lastName="Ihimaera"},
    {firstName="Patricia", lastName="Grace"},
    {firstName="Alan", lastName="Duff"},
    {firstName="Lee", lastName="Tamahori"},    // OK: not an author
    {firstName="Keri", lastName="Hulme"}
];

arraySort(
    a,
    function (e1, e2){
        return compare(e1.lastName, e2.lastName) > 0;
    }
);
writeDump(a);

Saturday, 27 July 2013

New Railo docs site

G'day:
Bam Bam Bam. Three articles in rapid succession this evening!

I shoulda said something about this earlier, but it slipped my mind. Mark Drew has been beavering away getting the new Railo online docs sorted out, and they're now live.

The site is here: Railo Documentation Viewer.

Wanna know the best thing? This is the URL for the docs on <cfassociate>: http://railodocs.org/cfassociate. I did not look that up, I just knew all I needed to do is to type the tag (or function) after the domain name, and it works. Contrast this with Adobe's equivalent docs. Take a deep breath... http://help.adobe.com/en_US/ColdFusion/10.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7f5b.html. Got that? Railo seem to have a better grasp of web concepts like SEO and URL hackability than Adobe have (well if Adobe does, they don't care).

On SEO... if I search for "railo docs cfassociate" the first link is the page above. If I try "coldfusion docs cfassociate" I get the CF8 version of the docs as the first link, and the docs for ColdFusion 7 are third. ColdFusion 9 and 10 don't actually show up on that search, but Google displays them as results for "similar" searches: "coldfusion docs cfassociate", then the CF9 docs are listed. Oddly, if I actually do a search for "coldfusion cfassociate", I get the CF8 match first, the CFMX7 match on the second page (who the hell every looks at the second page of Google's search results? ;-), and by the time I gave up... page 5... I'd still not seen a link to the docs for any currently-supported version of ColdFusion. Blimey on page 3 there was a link to this blog of me talking about the tag, but nothing from Adobe on CF10 or even CF9. Slack. How can a web company be so poor at making sure their own docs are googlable?

ColdFusion: Really, Adobe: "NotWorthEffort"

G'day:
So I started to write an article on the performance of evaluate() (and whether it's still a problem), but I got sidetracked by other CFML stuff I was catching up on, and now I'm on to article two of three articles to today that I'm writing instead. Mostly cos they're easier than the evaluate() one.

This is another one on toScript().

Whilst researching the earlier article, I looked to see what other bugs there already were with toScript(), to see if I was treading already-trod ground (no, as it happens). Ad I came across this bug, raised by Ray Camden a while back: 3041310. The gist of it is demonstrated by this code:

cfRainbow = ["Whero","Karaka","Kowhai","Kakariki","Kikorangi","Tawatawa","Mawhero"];
jsRainbow = toScript(cfRainbow, "jsRainbow");
writeDump([cfRainbow,jsRainbow]);

The result of this is:

toScript()? New to me. New bugs to me, too

G'day:
I'm in Portumna visiting my boy... well not right now, I'm in the pub starting my second pint of Guinness, but the point is I'm away from home and am on my baby netbook rather than my usual rig, so blogging / coding / internet access / my-ability-to-do-useful-things are all severely curtailed today.

That said, to kill time, I'm writing a coupla articles.

Friday, 26 July 2013

Recognising the people who help me with this blog

G'day:
Adam Tuttle (that's twice today) was quipping last that I'd mentioned him twice on the blog on Weds, and I should try for three times y/day (which failed: just the one mention, sorry). Anyway, as a joke back I said I should tag my articles with the people who I mention - these are usually people who have helped me, or caused me to write the article - and whoever is at the top of the list is automatically on my "give them beer next time I see them" list.

And then I thought... well why not? People do help me out and inspire me to write, and I appreciate it. So I did go through all my back articles last night, and tagged them up with the people I have mentioned in them, and I will continue to do so. So there are people's names listed at the bottom of the article on the "tags" bit, and the tag cloud thing on the right hand side also lists a bunch of people amongst the other tags I've got. Now there's been a whole swag of people tagged up, and the tag listing became unhelpful with every tag displayed, so you don't get a mention on the right unless you've got 3 mentions (or are Chris who only had two mentions until now, but as he wrote my most popular article, I decided to list his anyhow. And now he's got his third mention anyway).

Sean's currently at the top of the leaderboard, and there's a fair gap back to the rest of the pack.

Anyway... cheers y'all for your help along the way.

--
Adam

ColdFusion hoists VAR declarations

G'day:
Whilst messing around with how scopes in functions in ColdFusion work, I came across something I didn't know: like Javascript, ColdFusion hoists its VAR declarations. This could be common knowledge, but I certainly didn't know.

Here's some code that shows how an unscoped variable within a function interacts with a samed-named variable in the arguments scope, and to what unscoped references refer.

Update:
Ray pointed out the way I had originally done the code was very hard to read (harder than usual ;-). I'd tried to take a sort of two-column approach so all the writeOutput() statements were off to the right next to the statement they were outputting stuff for, not underneath. This made it much easier to follow the code in a text editor, but it looked whack when it was in the blog. I didn't notice when I was proofreading. Anyhow, I've sorted it out now. Cheers for the nudge, Ray. And note to any other readers: do let me know if I screw anything up like this!


function f(){
    writeOutput("Top: arguments: #arguments.foo#; unscoped: #foo#<br>");
    var foo    = "var";
    writeOutput("After var: arguments: #arguments.foo#; unscoped: #foo#<br>");
    foo        = "unscoped";
    writeOutput("After unscoped: arguments: #arguments.foo#; unscoped: #foo#<br>");
    return {arguments=arguments,foo=foo};
}

result = f(foo="arg");
writeDump(var=result);

Thursday, 25 July 2013

CFML implementation of Array.reduce()

G'day:
I've knocked out a quick arrayReduce() UDF, which I'll post on CFLib once I get some code review feedback (if any) on it. I'd appreciate sets of eyes on it, if you had time.

Update:
Adam Tuttle (only one mention today mate, sorry) offered some advice on Code Review, which I have taken onboard. This is the second iteration of the function. I have also updated the entry on Code Review too.

For the sake of completeness, here's the code, but pls feed-back on the Code Review page, as per link above:

/**
* @hint CFML implementation of Array.reduce(), similar to Javascript's one ref https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
* @array Array to reduce
* @callback Callback function to use to reduce. Will receive the following arguments: element (of current iteration of the all), index, array, (optional) result (of preceeding call to callback())
* @initialValue The initial value to use to start the reduction
*/
any function arrayReduce(required array array, required any callback, any initialValue){
    var startIdx = 1;
    if (!structKeyExists(arguments, "initialValue")){
        if (arrayLen(array) > 0){
            var result = callback(array[1], 1, array);
            startIdx = 2;
        }else{
            return;
        }
    }else{
        var result = initialValue;
    }
    for (var i=startIdx; i <= arrayLen(array); i++){
        result = callback(array[i], i, array, result);
    }
    return result;
}

If created a Gist with full(-ish) unit test coverage, too.

Ta.

--
Adam

CFML: Scoping or not scoping?

G'day:
I'm back to talking about CFML today, rather than griping about stuff.

The other day I blogged about posting my UDF replaceWithCallback() on CFLib. Someone (I can't find who it was and where it was even, so sorry for not mentioning you by name) Scott Busche, made an observation on Code Review that they were surprised I don't scope the references to arguments within the code, eg:

string function replaceWithCallback(required string string, required string regex, required any callback, string scope="ONE", boolean caseSensitive=true){
    if (!isCustomFunction(callback)){ // for CF10 we could specify a type of "function", but not in CF9
        throw(type="Application", message="Invalid callback argument value", detail="The callback argument of the replaceWithCallback() function must itself be a function reference.");
    }
    if (!isValid("regex", scope, "(?i)ONE|ALL")){
        throw(type="Application", message="The scope argument of the replaceWithCallback() function has an invalid value #scope#.", detail="Allowed values are ONE, ALL.");
    }
    var startAt    = 1;

    while (true){    // there's multiple exit conditions in multiple places in the loop, so deal with exit conditions when appropriate rather than here
        if (caseSensitive){
            var found = reFind(regex, string, startAt, true);
        }else{
            var found = reFindNoCase(regex, string, startAt, true);
        }
        // etc

Wednesday, 24 July 2013

weB LOG

G'day:
Just so you know... I never sit on any of the stuff I blather on about, or save it to make a particular point.

Whilst on the subject of things in ColdFusion that don't work

G'day:
I also found this one whilst testing Adam's code (as per previous article).

This doesn't work in ColdFusion (9.0.1):

a = 1;
b = 2;
writeOutput("Result: #a == b#");

I get:

Error Occurred While Processing Request

Invalid CFML construct found on line 4 at column 25.

ColdFusion was looking at the following text:=


But this is fine:
a = 1;
b = 2;
writeOutput("Result: #a EQ b#");

Obviously both should work fine. And they do on both Railo and OpenBD.

That's all I have to say on that. Bug raised: 3600686.

--
Adam

Stupid (but trivial) bug with htmlEditFormat()

G'day:
I was putting Adam Tuttle's htmlDecode() UDF through the approval process just before, and found a daft (and admittedly pretty inconsequential) bug with htmlEditFormat() whilst I was about it.

Things I dislike: rules for the sake of rules

G'day:
Rules are great things to have if there's a point to them. However rules just for the sake of it are just dumb. Here's a dumb rule ColdFusion has.

Tuesday, 23 July 2013

ColdFusion: I've come to a conclusion

G'day:
This is something that I've been mulling over / worrying about / contemplating for a coupla months now. I find myself poised over the keyboard frequently about to write this increasingly often. And Rob Glover is to thank for making the comment on Twitter that made me think "actually, yeah..."

This is my conclusion. I now believe Adobe is doing more harm to ColdFusion and CFML in general than they are good. And I think it's time - for the benefit of CFML as a language - for them to pull out. There's a few reasons for this.

Firstly, whilst they are "mothership", the language is just not going to go anywhere. As far as I can tell, no-one involved in the Adobe ColdFusion Team actually uses CFML as a matter of course, and I think the entire business unit is completely detached from the community. Worse, what they are doing on a day-to-day basis is Java. Whilst Java is a great language, it's not even remotely the same in intent as CFML is, and - worse - Java is a behemothemic (I made that word up) old juggernaut which works in a completely different way than the spritely languages that compete in the space CFML is struggling to be relevant in. Adobe don't (seem to ~) know how CFML is used, what the target market is, and what they should do with it to keep it relevant.

Secondly... $$$. I guess this makes sense. Adobe are not in the business of making a great language, they're in the business of making money for their shareholders. To do this, they need revenue, and to have revenue, they need to sell licences. Because they're in the software selling business. Unfortunately the people they market CF / CFML to are the people who pay for the licences, and all they need to do is to generate enough smoke, and angle the mirrors in the right way, and the licence-purchasing decision makers will re-up their licences. Especially if - and I think this is where the bulk of Adobe's ColdFusion revenue comes from - the person signing the cheques (dear god, I actually typed in "checks" then, initally) is doing so on behalf of some US government agency, and there's very little oversight on the sort of money they're paying; and what's more important to them is continuity, not innovation.

Thirdly. And this is how I came to this conclusion, remember, so this is the third reason that influenced me to think the way I do: Railo. I've been using Railo since it came on the scene, but only in the capacity of reading the mailing list and testing stuff out. I have never had any of my apps running in production on Railo. This is not slight on Railo, it's just that I have always worked for ColdFusion shops; licences already purchased. But over that time I've seen Railo arrive as the third possibly-viable CFML solution (after ColdFusion and BlueDragon... the New Atlanta one, not the Alan Williamson one, which came along around about the same time as Railo?), and then surpass BD (closed or open) becoming the second player in the CFML market, to now pretty much being the leader in the CFML community. I don't mean in revenue, or in "stature", but in importance. Basically I've gone from not giving a shit what Railo might have to say about CFML, to not giving a shit what Adobe might have to say about it (I never gave a shit about BD, for varying reasons). This is partly because Adobe hardly ever say or do anything sensible / useful about CFML other than when there's a release cycle on, and more recently because what Railo is saying about CFML and the CFML community just makes sense. Whereas everything out of the Adobe stable these days is going more and more heavily on the marketing smoke and mirrors, and is almost entirely without substance.

Fourthly I've been looking at other languages recently. I'm a slack-arse when it comes to forwarding my career prospects: I'm good at CFML and that finds me work. Well has done so far. I'm lucky in that regard (although I will never become a rich person with my work ethic, I at least have a place to hang-out and write code every day ;-). So I've pretty much focused on CFML and enough JS to get by over the last decade. However now that I'm looking, what I do see out there is every other relevant language just does stuff better than CFML does these days. It's a complete frickin' myth that CFML is still this quickest-to-market / rapid-development language. It might be so in the context of "my simple website", like what CFML used to excel at ten years ago, but in the context of doing more than "making it easy to generate HTML", even my superficial knowledge of competing languages reveals that CFML is basically irrelevant in that space now. Now I'm not saying some stuff on a line-of-code-by-line-of-code basis is simply easier to do in CFML, but I really think ColdFusion is lacking the bigger picture here. Plus it just moves too damned slowly to keep up. THat and when they do decide to do new stuff, it's crap like <cflclient> (ColdFusion 11's apparent marquee feature) which seems to be a wizard for writing JavaScript for mobile apps. In CFML. What? Are you lunatics, Adobe?

Update: <cfclient>? Wah?
Someone asked what I'm on about with this mention of <cfclient>. It was mentioned on a slide in a presentation I saw at cf.Objective() of new features coming to ColdFusion 11. There was also discussion of the CF -> Mobile functionality in the same presentation, and again at Scotch on the Rocks. I do not claim any specific knowledge about the functionality other than what's in the public domain via those sources.

The shining light here for CFML though, is Railo. They listen to their developers / their community. They do Railo (and accordingly CFML ~) consultancy, so they know what people on the ground are actually doing, and they enhance then language in ways that will benefit professional CFML developers. They seem to look at what other languages are doing, and consider those features for Railo / Railo's CFML. They keep the language alive and moving. They seem to give a shit about CFML (the Adobe ColdFusion Team seem to be interested in CFML because it pays their salary, and not really beyond that. Well that's my impression).

So here's what I think. Adobe? Give up on ColdFusion and CFML. You're not helping.

And Railo? Screw what Adobe might have done / might be doing with CFML. Take the language as your own (that sounds more melodramatic than I intend it to, but you get the idea).

What do you think?

--
Adam

Two things: I'm thick; and one cannot use a Mockbox mocked method as a callback

G'day:
And to think someone said on Twitter the other day that I'm one of those people who are never wrong (the subtext being "actually you are, but just will never admit it"). Well here's an example of me being wrong, happily admitting it, and admitting to being a bit stupid to boot.

Saturday, 20 July 2013

Reaction to / plug for recent 2DDU podcast

G'day:
This isn't some attempt at a controversial reaction to something someone else in the community has said, it's just that the 2DDU podcast blog is a bit exclusive in that it requires a Facebook login to comment. And I do not have a Facebook account.

Thursday, 18 July 2013

1

G'day:
I'm a bit late with this as it happened a week or so ago, but I just noticed I've been prattling away on this blog for a year now.

Apropos of nothing, here's some mostly useless information about this blog.

I said in one of my earlier articles that I've learned more about ColdFusion since I started this blog than I had in the preceding few years. This continues to be the case, and I've learned a bunch of good stuff about Application.cfc, ColdFusion regexes, JSON (grumble), REST, interfaces (in general, as well as ColdFusion's inplementation of them), and various other odds 'n' sods. Even some web sockets stuff, whilst troubleshooting that security issue from a coupla weeks back. I've also used Railo a lot more, and had a look at Coldbox. Beyond ColdFusion I've also started dabbling with PHP and Ruby. It's been cool! I hope some of it was useful to you, or at least slightly interesting. Or killed some time whilst you tried to decipher what I was wittering on about.

To close, I'd like to say special thanks to a few people whose participation in this blog has been helpful, interesting or thought-provoking. In no particular order, and it's certainly not an exhaustive list:
  • Chris Kobrzak
  • Sean Corfield
  • Andrew Myers
  • Bruce Kirkpatrick
  • Brad Wood
  • Andrew Scott
  • Adam Tuttle
  • Ray Camden
  • Gavin Pickin
  • Jay Cunnington
  • Simon Baynes
  • Duncan Cumming
  • Brian Sadler
There's been a bunch of great input / correction / sanity-checking / bullshit-detection done by a heap of other people too.  Cheers to everyone who's participated here.

And now on to year 2...

--
Adam

replaceWithCallback() UDF for CFLib and suggested ER for ColdFusion

G'day:
I was looking up the docs for Javascript's String replace() function the other day (because, no, I could not remember the vagaries of its syntax!). And whilst being relieved I had remembered it correctly for my immediate requirements, I also noticed it can take a callback instead of a string for its replacement argument. How cool is that?

Wednesday, 17 July 2013

Another day, another ColdFusion JSON bug (and a Railo bug too, just not with JSON)

G'day:
Well at least this time it wasn't something that caught me out. This one wasted someone else's time.

CFML: My CFLib approval process

G'day:
A week or so ago there was some derision expressed that when approving UDFs submitted to CFLib we might change the code (for whatever reason). There was also derision expressed at how long it had thusfar taken to approve that person's UDF (it's still in the queue, and as far as I'm concerned: there it will stay for the foreseeable future... pretty much until everything else in the queue is cleared).

But due to this conversation I figured I might explain what it is I do when I approve a UDF. I cannot vouch for the process that Ray undertakes when he approves a UDF... this is simply the process I undertake. And I might perform some, all or none of these steps, as I see fit when doing the approving.

Monday, 15 July 2013

π and strings

G'day
OK, an upfront warning: the degree of usefulness of the information herein is perhaps the most limited of all the articles I have written. So that's saying something.

CFML: getObjectMetadata()

G'day:
This is a strange article, this one. It started off just being a feature request which I was typing into the Adobe bug tracker, but decided to write my thoughts up here, by way of soliciting other people's thoughts too. On re-read just before pressing "publish", I realise the framing for the point is about five times longer than the point itself. Oops. Oh well. It's a slow news day here. It's Monday after all.

ColdFusion has a function getMetadata(),  which can be called on a variety of objects: an CFC instance, a user-defined function, a property, a query. I'm just focusing on on its usage with "objects".

Here's an example of getMetadata() being called on an object:

// Bloggable.cfc
interface {

    public void function g(required numeric x);

}

// Parent.cfc
/**
@hint Parent component
*/
component {

    /**
    * @hint Method defined in Parent
    */
    public void function g(required numeric x){
        return;
    }

}

// Sub.cfc
/**
@hint Demonstrating getMetadata() results
*/
component extends="Parent" implements="Bloggable" {

    /**
    * @hint Method defined in Sub
    * @x A number
    */
    public void function f(required numeric x){
        return;
    }

}

<!--- test.cfm --->
<cfdump var="#getMetadata(new Sub())#">

Friday, 12 July 2013

Contrast

G'day:
Three minutes to pub time.

Yeah, so it wouldn't be my working week without another CF vs JSON issue

G'day:
Poor old me, and poor old Mockbox.

I just found this one when testing Mockbox 2.1. Testing it to see if we could upgrade to it (no, not until this is fixed). We already could not upgrade to 2.0 due to another issue (MOCKBOX-6), so on 1.3 we stay for the time being.

Both of these issues have been ColdFusion problems that Mockbox has been victim of though. Which sucks a bit for Luis & co.

CFML: Trying to be sneaky and failing

G'day:
As I said in my previous article, it's a bit of a slow day in the office today, so I'm just sitting here hitting ColdFusion interfaces with a sledgehammer to see what happens. I was trying to be clever, and have failed miserably. Oh well.

When I was fiddling around trying to work out "WTF" in that previous article, I ended up with this code.

A vanilla interface:

// TestInterface.cfc
interface {

    public numeric function length(required string s);

}

A CFC which implements it:

// Impl.cfc
component implements="TestInterface" {

    public numeric function length(required string s) {
        return len(s);
    }

}

And some code that mungs around with it:

writeOutput("Initial state<br>");
impl = new Impl();
writeOutput('isInstanceOf(impl, "TestInterface"): ' & isInstanceOf(impl, "TestInterface") & "<br>");
writeOutput("<hr>");


writeOutput("After clearance<br>");
impl = new Impl();
structClear(impl);
writeOutput('isInstanceOf(impl, "TestInterface"): ' & isInstanceOf(impl, "TestInterface") & "<br>");
writeOutput("<hr>");


writeOutput("Restored to working order<br>");
impl = new Impl();

public numeric function length(required string s) {
    return len(s);
}
impl.length = length;

writeOutput('isInstanceOf(impl, "TestInterface"): ' & isInstanceOf(impl, "TestInterface") & "<br>");
writeOutput("<hr>");

Stand-alone repro case for that weird interface bug

G'day:
This is a follow-up to the article from yesterday detailing some weirdness I was getting when using Mockbox to mock methods in CFCs which implemented interfaces.

I have no factored-out Mockbox as a factor in this issue. The tangentially-related issues in Mockbox still stand, but I wanted to have a repro case that did not have any third party code in it. And now I have one.

Thursday, 11 July 2013

Weird issue with MockBox and interfaces: issue identified

G'day:
A coupla days back I wrote a vague article "Weird issue with Mockbox and interfaces", which detailed some weirdness I was seeing with using Mockbox to mock methods in CFCs which implement an interface.  The short version is that on the first use of a method mocked this way, I was getting this error:

coldfusion.runtime.InterfaceRuntimeExceptions$ArgumentsMistmatchException: Function argument mismatch.[etc]

However if one just re-ran the test, the problem went away. In fact it was only the first usage of the mocked method after ColdFusion was started that caused the problem. Subsequent runs: all good. Restart CF: problem. This is on ColdFusion 9 & 10, but not Railo.

Tuesday, 9 July 2013

Well done Adobe ColdFusion Team

G'day:
Hopefully you've heard there's a patch out for ColdFusion 10 (now version 10.0.11) for the web sockets security hole that Henry Ho noticed a week or so ago. I did some investigation on the issue, and identified four separate problems with the web sockets implementation on un-patched (10.0.10 and below) ColdFusion 10 installations.


The good news is that two of those four issues are fixed, and they are the two significant ones:
  • public CFC methods were callable via web sockets. Only remote methods ought to be externally accessible;
  • non-web-accessible CFCs were accessible via web socket requests, provided there was a ColdFusion mapping to them.
I've verified those are now fixed.

Weird issue with Mockbox and interfaces

G'day:
This is not gonna be a very well-realised article, as it's posing a question that I've not really been able to flesh out yet.

Esoteric bug in argumentCollection handling

G'day:
Here's some more shoddiness / stupidity in ColdFusion. And it's nothing to do with JSON or web sockets for a change.

Monday, 8 July 2013

Quick note: CFLib now accepting ColdFusion-10-specific UDFs

G'day:
This should possibly have happened a while back, but I suspect no-one's mentioned it until recently, so nothing was done about it.

When one submits a UDF to CFLib, one needs to specify which ColdFusion version it is for. It was possibly a bit of an oversight that until now "ColdFusion 10" was not on the selection list. It is now, and CFLib has had it's first submission (and approval) of a ColdFusion-10-specific UDF, from James Moberg: isImageCMYK().

There's a bunch of interesting stuff ColdFusion 10 can do with function-expressions, callbacks and closures, so perhaps you have a treasure trove of ColdFusion 10 stuff sitting on your hard drive that the community could benefit from? If so, please consider submitting it to CFLib. Cheers.

I'll have a poke around and see if I do. Although I've not done anything "production ready" on ColdFusion 10 yet.

--
Adam

Saturday, 6 July 2013

CFML: REST requests don't seem to correctly use Application.cfc either

G'day:
The emphasis in the title is an allusion to web socket requests also not respecting them.

God knows what this article will read like. I headed to the pub to catch the last quarter of the Aussie v Lions match, and I've been sitting here since. I'm over in Portumna visiting my son, and other than my 4h appointment doing that, I've kinda got nothing better to do here, so I'm still in the pub (which has wireless), amusing myself and drinking Guinness. Am just finishing my third pint. And will order a fourth. It's 3pm ;-)


Anyway, someone posted a question on Stack Overflow about how erroring REST requests don't fire onError() calls. This is a known issue: 3506757. But it was enough for me to furrow my brow and decide to have a look into it.

Now I'm no REST expert (neither in the context of ColdFusion, nor in general), so it always takes me a bit to get a REST CFC / function working, but I've come up with this test environment:

// Application.cfc
component {

    this.name    = "testappCfcIssue09";

    public void function onApplicationStart(){
        restInitApplication(expandPath("../api"), "api");
    }

    public void function onError(){
        writeLog(file=this.name, text="#GetFunctionCalledName()# called");
        writeDump(var=local, label=this.name);
        abort;
    }

}

This is a fairly vanilla Application.cfc. The onApplicationStart() stuff is just to work around some lazy programming on the part of Adobe, in that when one changes a REST-aware CFC, it will error until one restarts the REST engine. The onError() is just there in case REST calls call this onError() instead of the one in the directory the REST CFCs are in (I came into this not knowing how this stuff was handled at all, so just making sure).

<!--- testException.cfm --->
<cfhttp method="get" url="http://#CGI.http_host#/rest/api/Messaging/forceError" result="response">
</cfhttp>
<cfdump var="#response#" label="HTTP Response">

[I have decided against Guinness for the fourth: it's cider. It's a cracking hot day here today in Portumna, so not really Guinness weather]

This calls my REST method, and dumps the response. Now, over in the API dir, I have this lot:

//Messaging.cfc
component rest=true {

    remote string function gday(required string name restargsource="query") httpmethod="get"  {
        return "G'day #name#";
    }

    remote string function forceError() httpmethod="get" restpath="forceError"  {
        throw(type="ForcedException", message="Exception forced", detail="Because you asked me to");
    }
} 

The first method is just me testing that stuff was actually working properly with a vanilla method. The second method is one that forces an error, specifically so I can see what happens when an exception occurs.

And I have this Application.cfc in this directory as well, to log everything which goes on during REST requests:

// Application.cfc
component {

    this.name                = "restRequestLogging02";
    this.sessionManagement    = true;

    public void function onApplicationStart(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
    }

    public void function onApplicationEnd(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
    }

    public void function onSessionStart(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
    }

    public void function onSessionEnd(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
    }

    public void function onRequestStart(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
    }

    public void function onRequestEnd(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
    }

    public void function onRequest(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
        include arguments[1];
    }

    public any function onCfcRequest(required string cfc, required string method, required struct args){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
        var o = createObject(arguments.cfc);
        var metadata = getMetadata(o[method]); 
        
        if (structKeyExists(metadata, "access") && metadata.access == "remote"){
            return invoke(o, method, args);
        }else{
            throw(type="InvalidMethodException", message="Invalid method called", detail="The method #method# does not exists or is inaccessible remotely");
        }
    }

    public void function onError(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
        writeDump(local);
        abort;
    }

    public void function onMissingTemplate(){
        writeLog(file=this.name, text="#getFunctionCalledName()# called");
        writeeDump(local);
        abort;
    }

}

The only non-obvious method in this is the onCfcRequest() one. The code here is reflective of the unexpected "security issue" onCfcRequest() could effect in an application that I blogged about a while ago. Any onCfcRequest() method should do this lot as a minimum. That consideration aside, basically every event handler call is logged. That's the crux of things.

Friday, 5 July 2013

JSON: just to confirm my expectations aren't off

G'day:
I'm half-way across the Irish Sea at the moment (oh, now that I look out the window, I've actually just making landfall over Ireland), and sitting in a very cramped seat trying to write code on my netbook without elbowing the bloke next to me too much. So this will be short and to the point. And hopefully the last chapter in all this JSON nonsense.

Thursday, 4 July 2013

Right... so JSON is being a pain in the arse again

G'day:
You might have seen my Twitter status update earlier today, which went kinda like this:

#ColdFusion.... JSON... AGAIN... AAAAAAAAAAAARRRRRRRRRRRRRGGGGGGGGGGGHHHHHHHH!!!!!!!

Response to comment (since redacted, it would seem) posted on the Adobe ColdFusion Blog

G'day:
I headed over to the Adobe ColdFusion Blog where there's an article on these security holes in ColdFusion (via web sockets) to reply to an update Awdhesh made yesterday. But the comment is gone. Well no it's not, here it is:

Repro case for "contains" pseudo-reserved-word interfering with Mockbox

G'day:
This is mostly for Brad Wood, but it might be of passable interest to others, so I'll plonk it here.

Yesterday's article discussed how contains is kind of a reserved word, but kind of not in ColdFusion (it's just not in Railo). I observed to Brad that this actually bites us on the bum with Mockbox, and he asked for more info, so here it is.

Basically we use a caching system which has a method "contains" which checks to see if there's an item with a given key already in the cache, before trying to fetch it. We've actually since revised this approach, but we have some legacy code still using methods called "contains". So we need to unit test them, and indeed their responses play a part in other methods we test. When testing these other methods which use the caching system, we mock-out the cache, and the methods within it, and we use Mockbox to do this. Mockbox is cool, btw. You should be using it if yer not already.

We're still using Mockbox 1.3 (more about why further down), and it is impossible to directly mock a method called contains using Mockbox. We've worked around this, but it took a bloody long time to work out what the hell was going on, and that there was working-around to do.

Wednesday, 3 July 2013

Threads add weird methods into CFCs

G'day:
This is a weird one. And not terribly interesting, but a blog is supposed to be a log of what I'm doing, and this is what I'm currently looking at.

Reserved words? Or not? Make up yer mind

G'day:
What a busy CFML day today (ed: it's now the following day... I didn't get this finished last night) has been. And it seems it just keeps on giving. Here's a quirky one that has bitten me on the butt before, and has now bitten the butt of someone on Stack Overflow too.

Official confirmation: Adobe is on the case regarding ColdFusion 10's web sockets security issue

G'day:
SSIA, really. But you know me: I can pad 14 words of information out to take 1400 words to say...

This is in reference to the security holes that were discovered in ColdFusion 10's web sockets implementation a few days ago, as I discussed in an earlier article: "Web socket security issue: risk assessment & findings".

Rakshith posted on Twitter & on the Adobe ColdFusion blog today that a fix is in the pipeline. He does not go into any details as to whether they're fixing all the issues identified, or some, or what: I guess time will tell.

However I'm pretty impressed with their turn-around time on this one. Henry raised the issue on June 27, and it's only a week later and they're got a patch in the works (I presume it's well under way, not that they were simply starting it when Rakshith announced it).

I look forward to testing it, and I will feedback with my findings having done so.

Not bad: only 180-odd words to re-articulate the necessary 14 ;-)

--
Adam

Tuesday, 2 July 2013

Daft bug with objectSave() and XML

G'day:
Ever have one of those days in which you spend most of your time fighting with ColdFusion, rather than getting your work done? Well if so, you're familiar with the last week of my life. And it continues...

Example of serialising data using objectSave() and objectLoad()

G'day:
Bloody hell: fifth one today. And I've got a sixth one to come (another bloody stupid ColdFusion bug... stay tuned...).

My previous article described the mooted solution to our serialisation challenge, but it did not have an end-to-end code example.

There's always another bug...

G'day:
Whilst playing with objectSave() and objectLoad() yesterday, I came across yet another bug in ColdFusion. It really does seem sometimes that if one attempts to do anything other than the "obvious" in CF, there are bugs. Or illogical quirks. Or omissions in the implementation.

This is just a bug (NB: in ColdFusion 9.0.1+. But not Railo).

Serialisation woes resolved: "screw JSON"

G'day:
(third one today... the other two were only wee ones, so hardly count).

Right, so you might've read me wittering on about "ColdFusion vs JSON. How to make a developer cry". I needed to serialise some data to put it in a cache, and ColdFusion 9's JSON-handling is not up to the job.

What were were trying to do is to reliably serialise a query, and have ColdFusion not mess up either the null values, nor the dates (actually the latter is a side-effect of the JSON processing, and more an issue with query of query).

I was trying to use JSON because it was faster and lighter than WDDX, and it's kinda become the de facto way of doing quick serialisation, I think.

Yesterday I was forced to write this email to my superiors:

Adobe ColdFusion bug tracker: sigh

G'day:
 (Warning: this is just a frustrated grumble, and has little merit beyond allowing me to stomp my foot like a petulant school child).

As mentioned in my previous article, "I've raised four bugs, but unfortunately I cannot share the details with you because I raised them as "security issue", so I don't get given the bug number."... all I get is a note "Thank you for submitting a bug. Due to security concerns, this bug will not be externally viewable."

This plumbs new depths of "Adobe really just don't think shit through".

ColdFusion: WebSocket security issue: status update

G'day:
Just a quick one. There's been some feedback from Adobe regarding this web sockets security issue. As a comment against that article, Awdhesh says:

We are working on it and the fix will be available in next updater for CF10.

Monday, 1 July 2013

Question for y'all re CFML interfaces

G'day:
I can't work out what my opinion is on this.