Adam Tuttle

Do Not Hire: James Harvey "WebDevSourcerer"

If you see this man, do not hire him. He is of low moral character and not to be trusted.

I've taken this above photo of James Harvey from his own About Me page (google cache), and copied it to my own server, without his permission, on the off chance that he takes it down or renames it to try and remove it from this entry. He's already proven he's capable of worse.

Indeed, before I could even finish writing this entry he seems to have taken his whole website offline.


Over the last few days, a few of us have discovered and begun investigating what seemed to be recurring plagiarism of authors and works in the ColdFusion community. It all started with Adam Cameron noticing someone stealing his CFScript documentation and claiming it to be his own work (google cache).

When prodded about it, James Harvey, who also goes by WebDevSourcerer on Twitter and on Github, simply removed the offending blog post without so much as a, "Sorry about that!"

Of course we're still digging!

So far, Sean Corfield has found two more instances: One in which he claims that he wrote (google cache) some code that Nathan Strutz posted. Since I expect he'll take that down too, here's a screenshot for posterity: (click for full size)

And the second instance that Sean has found so far is one where he is claiming a stack overflow answer by Tony Petruzzi to be his own (google cache). Screenshot for posterity: (click for full size)

As I've started writing this, it looks like Kev McCabe has found another instance, where James has ripped off (google cache) content from Ray Camden. Again, screenshot for posterity: (click for full size)

And that's where I'm going to stop. ...For now? For the canonical list of things that we know James Harvey has plagiarized, check Adam Cameron's similar blog entry: Public service announcement: James Harvey / @webdevsourcerer is a systematic plagiariser

Why am I so riled up about this? Because I have spent almost 8 years creating free (not always great, but always honest!) content for the ColdFusion community. I would be absolutely sick if I found any of my content on his website with his name on it. (And believe me, James: I'm on the hunt.) And these are my friends he's been ripping off. I know I've had beers (or other non-alcoholic bevvie's in Nathan's case) with 3 of the 4 victims mentioned so far and I'm about half sure on the 4th.

Furthermore, in this industry without your integrity you're nothing. James Harvey? You're nothing, buddy. I hope all of our announcements about your abhorrent behavior outrank every other page about you on Google, and every potential employer you ever apply to Googles you. I hope your self-aggrandizing behavior was worth it, because it might turn out to have been career suicide.


To anyone else that happens to be named James Harvey and be unfortunate enough to work in the IT industry, I feel sorry for you. But this must not stand. That's a big part of why I've included his photograph. At least you can have facial comparison on your side...

Published 2015-01-25 @ 04:33 in Meta

Save Elvis! er, I mean ?:

Today was an interesting day.

I got an alert from the Adobe Bug Tracker, as I often do, to let me know that there was a change to one of the bugs I created / voted for / commented on. I clicked through to see what bug it was and what was new. It was a new comment on this beauty (CF11u3 broke the ?: operator) from everyone's favorite CFML curmudgeon (I think?), Adam Cameron. He asked what the status was for releasing the fix.

"A valid question!" I thought to myself, considering the bug was marked fixed on December 22nd, 2014. What gives, Adobe? Where's the fix? So I tweeted it, too. I believe strongly that the prescribed methods of communication should be used, until you start getting ignored or otherwise let down. Then, if the cause is just, it's time for a small dose of public shaming:

A few tweets were had back and forth between myself, Elishia, and Anit, with the end result being that the fix is scheduled for the next regularly scheduled update. Sounds great, right? Except updates are quarterly. Who wants to wait 3 months for a regression to be fixed? Is that even reasonable for a commercial platform? I don't think so, personally. Here's what I wrote in a comment on the bug, following that conversation:

I've already stated this elsewhere but just to put it on the record: I think that waiting for a quarterly update to fix a regression Adobe caused is terrible.

You say, "If you need a fix sooner, please contact our support team to request a private fix for this."

This implies that you have or can fairly easily create an update that fixes the issue. Why not just release that as update 4 and push all of the other non-update3-regressions back to update 5 on its normal schedule?

Further, "If you need a fix" can be rewritten as, "If you're using this feature" and I can assure you that either people have rolled back to update 2 or removed the feature from their code, because having syntax errors in your code is not just something you can leave be while you wait 3 months for a fix.

Either one is a losing proposition: There were things that are very nice to have included in Update 3 (many, many things, if my memory is correct. And if so, good and thank you for the update!) so rolling back to Update 2 denies us of useful features and bug fixes JUST like staying on Update 3 would deny us of this feature.

And lest you think I'm just being a jerk-nose for the sake of jerk-nosing...

Alas, that was the last they seem to have to say on the matter. Unacceptable, in my opinion. They did, however, offer to provide a "private fix" (their words) for anyone that asked.

For my part, while I do want it fixed, I don't consider myself special. I don't have a support contract — aside, you know, from the fact that CF11 was just released and is still in core support. So I'm not requesting a "private fix." But that doesn't mean you shouldn't.

In fact, I had the idea that we should more or less flood them with (legitimate) requests for the "private fix" in hopes that, if we can get enough, it will be easier for them just to go ahead and release an out-of-band patch. Also known as "doing the right thing."

So I made a form for you. Just click on over here, enter your name and email address, and submit the form. They'll be emailed, and you and I will be copied. (I keep a copy for posterity. Don't want any funny business with fudged numbers...)

Please feel free to share the link, and please only use the form once. It does not make any attempt to prevent duplicate submissions, and in my estimation they'll do more harm than good, so let's just keep things on the up and up, ok?

Published 2015-01-21 @ 04:34 in ColdFusion

In Response to CFHour #226

The latest episode of the CFHour podcast mentioned me in two consecutive segments, and in the latter they said that they fully expected me to respond... And who am I to let down a podcast to which I regularly listen?

First, they very kindly mention that I wrote a book! Thanks for that mention, guys. If you had reached out to let me know you were going to mention it, I would have been more than happy to offer a discount code for your listeners. Alas, I'll have to give that out here instead. CFHour listeners can save 10% by using this link.

The second segment was about my previous blog post, detailing the reasons that we gave up attempting to switch to Railo.

ORM Mapped Superclass entities named the same

As I mentioned in my previous post:

using MappedSuperClass with ORM entities that have the same name (e.g. User and base/User) — for reasons I don't feel like elaborating on but I assure you are completely legitimate — causes a Stack Overflow error

I guess Scott didn't accept my assurances of legitimacy. Not that I blame him, I'm always a skeptic, myself...

Scott: I guess I kind of understood, but I would never think to do that. Like, I would probably name the base user cfc, baseUser.cfc
Dave: Mmmhmm
Scott: I've used MappedSuperClasses a lot in the stuff that I do. As a matter of fact, all of my ORM entities extend a BaseEntity that has common methods that I want available to each one of the entities.
Dave: Yeah, most of mine do too.
Scott: I would just never think to name them the same name. Or more to the point, I wouldn't think to not name them base{Blah}, as part of the actual name itself—
Dave: Yeah
Scott: —rather than—
Dave: Uh-huh
Scott: —using a path to delineate them.

Ok guys, here goes. We've got lots of entities, and this system is only fractionally complete. We're sitting on around 50 entities right now and can easily see that expanding to be several hundred. In addition to that, this is for a resellable product, not a one-install bespoke system. So we're writing with customer-customizations in mind. Our customers are universities and colleges across the country (and maybe eventually the world?) and I'm sure you realize that they all have their own favorite way of storing data and their own favorite custom fields; so we're planning ahead to support that sort of thing.

Maybe User was a poor example. Universities tend to refer to their students, alumni, and sometimes even faculty and staff, as Constituents, so let's go with that. Uni A wants an isFaculty flag, an isAlumni flag, an isParent flag, and so-on (not a terrible idea, considering it's possible to be all 3 of those, and more)... Meanwhile Uni B might prefer to just have a type_code column with coded values to represent every possible case. Neither is objectively wrong, and it would be a crappy reason to lose a potential customer if we couldn't support their desired data formats.

So how did this drive the naming decision? Well, with potentially several hundred entities on our hands, our first goal was to set things up so that customizing the entities for one customer didn't make it difficult to maintain the source code (remember, this is all going into git) for other customers. So the strategy we decided on was to put the system properties in the MappedSuperClass orm/base/Constituent.cfc, and the custom properties into the child class orm/Constituent.cfc. Each customer will have their own branch of the repo, so the child class will stay empty in the master branch and the customer modifications to it will be done only in the customer's branch. This should (in theory!) practically eliminate merge conflicts (at least in the case of ORM entities), and keep each branch pretty clean.

We wanted to make maintenance as easy as possible, too. I suppose baseConstituent.cfc is just as easy to remember as base/Constituent.cfc — no argument there. But what happens when an entity legitimately needs to start with the word "Base?" Sure, we can have BaseBaseSalary.cfc (Yes, that's completely contrived: Why would you need a Base Salary entity? I've no idea!) but that just has a certain smell to it that I'm not fond of... like milk that hasn't quite expired yet, but still makes you think twice before drinking it. So instead we stuffed them in a base/ folder and called it a day.

As you've probably figured out, this was something that we were able to deal with relatively quickly: It wasn't "the last straw," so it had already been resolved by the time that last straw was reached. I do have to say, though, that I was very happy to revert the commit that made this work on Railo and go back to my base/Constituent.cfc strategy.

CFImport vs. App-Specific Mappings

I think we're mostly of the same mind on this, so just one quick note here: I think that the only way I'll consider this functionality "fixed" is when mappings are respected in all cases on all platforms. Anything short of that is a cop-out for what amounts to "academic" reasons. (If you're asking yourself what constitutes "academic" reasoning, think about your ipod playing the same song 5 times in a row in shuffle mode even though you'd hate that, because that's how random numbers work sometimes... Academic reasoning is not always the best.) And I don't think CF11 has, as you mentioned Scott, "fixed" (removed) the include-based workaround. I'm pretty sure it still works fine for me on CF11, though I did not revert the code changes I made to get this functionality running on Railo.

Metadata

Scott: This is the one that really had me kind of scratching my head

No worries, I'll explain!

Your description of the problem is dead-on: We've got functions with a definition like this:

function remove( rc ) iq:role="users:delete" {}

In fact, nearly every Controller method looks like that. It's the configuration necessary for our role-based security implementation. We combine this metadata with FW/1's before() lifecycle event to authorize (nearly) every pageview / form submission / etc a user makes in the application. This makes the code very terse, which is a particular passion of mine, but does come with its own challenges. For one, getting that metadata is on the "expensive" (in computational time) side. Again, we have to make use of a base class, but in this case it's just a generic baseController that all controllers extend:

component {

    function init(){
        var md = getMetadata( this );

        for ( var fn in md.functions ){
            if ( structKeyExists( fn, "IQ:ROLE" ) ){
                variables.permissions[ fn.name ] = fn['IQ:ROLE'];
            }
        }
    }

    function before( rc ){
        //if the requested action requires user to be logged in, verify that they are
        verifyLoggedIn( rc.action );

        //if user is logged in, check role-based security for the requested action
        verifyPermissions( framework.getItem() );
    }

    function verifyPermissions( 
        methodName
        ,failMessage = 'You do not have access to the requested functionality'
        ,jailEvent = 'main:jail/home'
    ){
        //action doesn't have role requirement
        if ( !structKeyExists( variables.permissions, methodName ) ){
            return true;
        }

        var requiredRole = variables.permissions[ arguments.methodName ];
        if ( session.user.isAssignedRole( trim(requiredRole) ) ){
            return true;
        }

        request.context.requiredRole = requiredRole;

        framework.redirect( arguments.jailEvent, "requiredRole" );
    }

}

When each controller is initialized (infrequently) it grabs a copy of its own metadata (see init()) and stores a copy of the iq:role metadata in the component instance (variables scope) for later reference. Then, during the before() lifecycle event, it is referenced to make sure the logged in user has access to the requested action, before the action happens.

Apologies, that was a rather long-winded way of getting to this next part: Why is the metadata named iq:role? Why not iq_role? The short answer is: Because when you can, it makes sense to... And you can on Adobe CF10+... So I did.

In a few more words: Namespacing. You never know what crazy feature the platform vendor will come out with next, right? I mean... CFClient, anyone? So by appending custom metadata to your functions, you run the risk that one day they will release a new version that you really want to use, but now they have revitalized <cflogin> to actually be relevant, and oh by the way, it uses a role metadata attribute on your functions. So you have to go change every function that uses your custom role metadata attribute if you're not lucky enough to have been using it "their way" before they were. By prepending some characters that are unique to your application, you reduce the risk of a collision later down the line. Just to be clear, the extra characters don't change the functionality in any way, they just reduce that risk of collision. That's what namespacing is for, but it still doesn't fully explain why ours uses a colon instead of an underscore or something else.

And the answer to that question is: because I said so! When you're in charge of decisions like that (and I am, right now), you sometimes get to pick things because you like the way they look. On Adobe ColdFusion, iq_role and iq:role are functionally identical, so I picked the one that I found more visually appealing. I can't really explain why I find the colon more pleasing to the eye, but I do. I probably saw it somewhere else (Java? Python? Who knows...) and it stuck with me. And it continues to serve us well on the ACF platform.

Taffy users are also probably familiar with it, as Taffy supports both taffy_uri and taffy:uri metadata attributes for specifying a resource's uri mapping (for both Railo and ACF8-9 support reasons).

So why doesn't Railo support it? Because they also support this: role:"foo". At some point in the past they chose to accept a colon as the delineator between the LHS and RHS of attributes, and as a consequence they can not support colons in the LHS. For them, it is like trying to write: iq=role="foo" — and I accept that. That is a particularly hard problem to solve, and I don't blame them for not considering it a high priority. I find it unfortunate that they dug that hole for themselves, but there's no use dwelling on the past: This is the world we live in, so this is what we have to deal with. Doing otherwise would be like fighting gravity.


Scott: You know, when people run into these problems and I don't, I can't help but think, "Are they doing something really weird, or am I doing something really weird?" Are they doing it wrong, or am I doing it wrong? And you know, when you're talking about me and Adam Tuttle, I would err on the side of me doing it wrong.

That's very kind of you to say so, Scott, but I think that this time it's very clearly my fault that it doesn't work on Railo... I didn't originally make the choice with platform agnosticism in mind. But would I go so far as to say it is "wrong"? I think not.


One last thing about that previous post ("We Tried (and Failed) to Switch to Railo")... While it was not meant to be putting down Railo in any way, I think it would be easy to see it in that light. I actually really think Railo is a great option; even more so if you get to start with a clean slate. But I just wanted to put out some information that showed a first-hand account that switching from one platform to the other is not all unicorns and rainbows. There is significant work involved. So when the fanboys spout off on mailing lists that you should just switch, you can rightfully start ignoring them. It's not that simple.

Scott had mentioned toward the end of that segment that he was impressed that these were the only issues that we ran into, and there is some truth to that statement, but probably not the kind he intended. It's more like the truth that your keys are always in the last place you look for them because you stop looking when you find them. We gave up once we ran into the metadata issue. It's possible that could have been the last thing; but it's equally possible that there could have been 30 other differences to deal with once the metadata problem was resolved.

In truth, we were working at a running pace. Steve was about to leave for a road trip and I was head-down on something unrelated, and while we were switching to a new AWS EC2 instance, we thought it would be a good opportunity to give Railo a try. We didn't really have the time to deal with a lot of issues that needed sorting out, so our threshold for conversion pain was pretty low. It ended up being about a 2 hour experiment that proved it wasn't going to be quick and painless to switch. Would we make these changes down the road and try again? We might... When we have time available to deal with it.

Published 2015-01-19 @ 09:40 in ColdFusion

We Tried (and Failed) to Switch to Railo

One of the things we wanted to do with our new product was to try running it on Railo. We've been developing it against Adobe ColdFusion 11 for a while now, but have wanted to try our code on Railo for a while. It would be nice if we could eliminate the license cost. We figured there would be a few things we would have to deal with, but we'd just fix those and move on. "How bad can it be?" we asked ourselves...

Pretty bad, apparently.

ORM Inconsistency

The first issue we ran into was that using MappedSuperClass with ORM entities that have the same name (e.g. User and base/User) — for reasons I don't feel like elaborating on but I assure you are completely legitimate — causes a Stack Overflow error.

So I renamed the base entities to xUser/etc, for what felt like ~200 entities (oh and all of the extends= referring to those base classes), but in reality was probably more like 50.

Class vs JAR

The next thing we found out was that Railo won't load .class files out of the classpath (as Adobe ColdFusion will), only .jar files. Fine, zip them up, rename to jar. A nuisance, but not the end of the world...

CFImport vs. App-specific Mappings

Having cleared those issues, we next found that Railo doesn't respect application-specific mappings in the taglib argument of the cfimport tag (don't believe the status=resolved, it's not)...

Ok, fine, we got lucky and were only using this in a few places. I fixed those, on to the next speed bump.

The Last Straw

We use a neat, custom, home-made solution to do role-based security throughout our application applying restrictions in the global FW/1 before() event shared by all of our controllers. Essentially, each FW/1 controller action method can optionally have a bit of metadata like iq:role="users:delete" and if the logged in user doesn't have this role, then they are redirected away from the action.

Unfortunately Railo handles metadata differently, and rewriting this portion of our code would be pretty drastic and take time that we don't have to invest right now.

That was the straw that broke the camel's back. We're going back to Adobe ColdFusion... for now.

I'm really disappointed. Certain people have been espousing Railo as their go-to CFML server for so long that I figured it would be pretty smooth sailing. (No ill will directed at you chaps... You're not to blame!) While Railo certainly does have a track record of responding to (some?) bugs much more rapidly than Adobe, their track record for feature parity is apparently pretty low.

In fairness to Railo, if we had spent equal time developing against Railo as our base and then tried our code on Adobe ColdFusion, it's equally likely we would be having similar problems. I just wish there was better parity and that old bugs weren't simply written off and ignored the way they sometimes seem to be.

Guess we'll keep using ACF until we're ready to switch to Node. ;)

Published 2015-01-06 @ 08:00 in ColdFusion Railo