This afternoon I was working on a set of persistent entities called event, eventtype, eventstatus, and eventpriority. Event is the "core" entity with hooks to status, type, and priority. When everything was done I whipped up a quick test:
<cfset o = entityLoadByPk("event", 597878)>
I assumed this would work - or throw an error about one of the properties perhaps. What I did not expect was this:
Mapping for component event not found.
That made no sense. I took a quick look at the Hibernate APIs to see if there was a simple way to list all known entities. I discovered getAllClassMetadata, a method of the SessionFactory, and quickly tried the following code:
<cfset factory = ormGetSessionFactory()>
<cfset known = listSort(structKeyList(factory.getAllClassMetadata()),"textnocase")>
<cfoutput>#known#</cfoutput>
This returned a nice list of entities - which unfortunately did not include event. At this point I was completely stumped. I had - of course, ran an ormReload() on my application. I even ran an applicationStop(). None of them threw an error, but none of them made event become recognized. Then I decided to check my ColdFusion logs. I found this gem in application.log:
"Skipping file c:\yada\yada\yada\event.cfc as it has errors"
What the frack?!?! So the component had an error - and the ORM system noticed this and just didn't tell me? How incredibly frustrating and counterintuitive! I checked exception.log and luckily, it had a longer error. My stack trace said:
"Properties cannot be declared more than once."
I quickly went back to event.cfc and saw the error right away - two properties with the same name. -sigh- Fixing that immediately corrected my problem.
Archived Comments
Ray, that might be related to CF supressing compilation errors in CFCs when it's searching for persistent entities. If that weren't the case, we wouldn't be allowed to have any unfinished (with known errors) CFCs in our application even if they had nothing to do with the operation being tested.
I'm assuming the "Skipping file c:\yada\yada\yada\event.cfc as it has errors" log entry was written when the event.cfc was being checked to see if it were a persistent entity. Since it couldnt compile (and therefore pull the metadata), it was skipped just in case it wasn't relevant to the current request.
I'd argue that hiding the error causes more problems than ignoring it. Your example - of saving an incomplete file - is probably much more rare than an honest to goodness error. And imagine if it did throw an error. So for you, it would be annoying once. You would comment out your file, rename it, whatever. But for me - I was completely lost. I was never told I had an error and had no idea where to go. I -really- feel that CF should err on the side of being more vocal here.
I disagree. Imagine not being able to run an app because of some invalid orphaned CFC. You're saying that CF should throw an exception because it wasn't able to parse a file that is never executed (just happens to be laying around in the web root somewhere) ? Or what about other sections of the web site that are a work-in-progress. We can't even test out the finished modules because our work-in-progress sections have errors? You'd also have to remove any TDD CFCs that happen to be in the web root because they'll error if the real CFC hasn't been written yet.
The fact of the matter is, you shouldn't be stopped from running the app because of an error'd file that's not even part of the request... that'd be very confusing and frustrating.
In any case, you're a bit late, for this was discussed quite a bit in beta...
Yes, I do think the app should come to a halt. At least you _know_ why it came to a halt. Don't you think that is preferable then _not_ knowing? I mean consider someone learning ORM. Where is this feature documented? Nowhere, right? So all of a sudden he can't use a CFC and he has no idea why. I find that to a much worse situation then you not being able to store 'works in progress'. If I had a file like that and it was in the way - the solution would be simple. Just comment it out. I'd remove the comments later when I have time to fix it.
As for TDD CFCs - are they persistent="true"? I'd be surprised if so.
p.s. As to it being discussed in beta - I have some vague memory - but again - consider the _normal_ developer. S/he is going to have no idea why his entity doesn't work. Zip. I think I got lucky by noticing the message in application.log. Do you think everyone will be that lucky?
Well, for what it is worth, Devin the anonymous, has pretty much sealed his argument with this statement:
"The fact of the matter is, you shouldn't be stopped from running the app because of an error'd file that's not even part of the request... that'd be very confusing and frustrating.
In any case, you're a bit late, for this was discussed quite a bit in beta..."
Seems to me, your file is part of your application and the part about being discussed in beta, is totally without merrit. That is as childish as saying 'My dad can write better code than your dad.'
"As for TDD CFCs - are they persistent="true"? I'd be surprised if so."
Ray, It doesn't matter. I'm saying that ANY CFC (persistent or not) with an error would cause the entire app to not run at all. The CFC must be compiled in order to pull the metadata to even check if it's persistent or not. If it compile it to make that check, and doesn't skip it, it should bring down everything else?
"consider the _normal_ developer. S/he is going to have no idea why his entity doesn't work"
That's what the logs are for, right? Is it not standard practice to check the logs when you can't figure something out? Do you think a _normal_ developer will understand why running index.cfm throws an exception because SomeOrphaned.cfc (which is never executed anyway) has an error?
@Gary: If you believe that every file would be part the application, then my dad probably can write better code than your dad.
The file may or may not be part of the application. If you don't make use of the Application's cfclocation setting, then it scans the entire web root for persistent CFCs. Many people run multiple applications in the same web root (especially when you have no choice, like in shared hosting).
Think of these situations:
You're running your main site as well as BlogCFC, Lighthouse Pro, and a few other mini apps in your webroot. BlogCFC happens to have a syntacticly bad CFC in there somewhere, which rarely gets executed, and therefore wasn't an obvious bug to catch (not that Ray ever writes bugs). Because of that, it brings down the main web site, the bug tracker, and everything else you have running in that webroot.
A lot of people use their CF account to host test scripts, demos and even a playground area. Their production apps would probably all be toast in that situation.
P.S. to Gary,
I wasn't trying to be anonymous. Ray has my email, and I'm assuming he knows who I am (maybe not). I think I met you at the Denver Flex User Group awhile back (I'm the co-manager). Or was it the Denver CFUG...
Oh, that Devin. Yes, It was the Flex group and I need to attend more so I can someday write other than CF code. On a side note, I'm not a fan of ORM. I view it the same as Front Page was to HTML, as ORM is to SQL. It will never, ever, ever write SQL code as well as I do.
And maybe I am wrong, but I would prefer it throw an error and let me comment it out or fix the error. And let us not forget that a lot of programmers don't have access to the logs on production servers, even test servers. At my shop, I am the only one that has access to the logs for security reasons. I'm not saying that is the corredt way, just the way it is.
@Devin: I only checked the logs out of desperation though. The error said that Foo wasn't an entity. It said _nothing_ about it having bad syntax. For all I knew, I was misspelling Foo, or doing something otherwise dumb.
When I use ORM, yes, I'd be willing for any 'bad' CFC to break the app. Yes, I would. :)
To your example - how would a bad BlogCFC bring down a bad LHP? By default, ORM apps check the current folder and subfolders under it. So subfolder A would not bring down subfolder B. And guess what - if subfolder A killed parent folder X, and if it was obvious, I'd know immediately how to fix it. I'd comment out the CFC. Problem solved.
" how would a bad BlogCFC bring down a bad LHP? By default, ORM apps check the current folder and subfolders under it."
Imagine this filesystem structure:
shinynet (webroot)
-- Application.cfc (for main site - ormEnabled)
-- index.cfm (and rest of main site files)
-- blog
---- Application.cfc (for blogcfc)
---- BadFile.cfc (can't be compiled due to error)
Requests to shinynet.com/index.cfm (or any request to the main site) would throw an error because of the blog.BadFile CFC, even though a request wasn't being made to the blog.
I know you believe that it'd be quicker to just fix any problems that are unrelated to the code you're trying to run, but the idea of having a piece of logic so dependant on other files that have absolutely no relation to it, and not even part of the same request, smells horribly, horribly wrong.
Just my opinion and I'm glad things were changed to work the way they do.
I'm not denying that child folder A would break /, I had thought you were arguing it would also break child folder B. So let's skip past that.
At this point - I'm done. I'm right. You're wrong. I'm rubber. You're glue. Etc. ;)
Regardless... this is an interesting debate and I have more knowledge now. Just another reason, for me, not to use or trust, ORM.
Thanks to both of you.
Woah now. As much as this bug ticked me off, I'd _definitely_ recommend using ORM. It is one of the most significant features ever added to CF - right after CFCs. I'd _strongly_ urge you to reconsider.
Though it was discussed in beta and we made the change considering so many folks wanted it to be the way it is, I agree that it is causing more debugging issues than helping. In fact lot of folks including Ray and *me* have been bitten :-)
So this is something that is worth re-consideration. We might just take a middle ground instead of reverting it completely.
Well, okay, I will reconsider. However, I do not see myself EVER letting an application write hidden SQL code for me.
You must like writing SQL then. ;) Personally I hate it. I also hate having to update table structures while working on a new site.
Also don't forget you can enable logging for Hibernate. That will "unhide" the SQL.
Maybe a good middle ground, as Rupesh mentions, would be an application setting that either skips malformed CFCs or throws an error when encountered.
However, doing the some bare minimum testing of the CFC, such as running a quick CreateObject() on to make sure it's able to be instantiated would avoid this type of problem.
@Ray: As you know, the database and SQL code can make or break the application. With todays applications SQL is an extreamly important part and letting some some other code write the SQL code is not a wise decission.
I liek the fact that ORM can handle much of the database work, but let me tell it what to do.
I couldn't agree more. This has bitten me a couple of times. It is wak behavior. If my code is broken... please tell me my code is broken. Good post.
@gary. I'm wonder why a lot of people seem to regard ORM as a 'replacement' for SQL? For me we will always have to write complex queries, mainly for reporting and let's face it that's what SQL is mostly about. Some reporting queries are among the most complex programming constructs I've ever had to get my head around (go to Joe Celko if you want to see what I mean). For CRUD activities though SQL is pretty repetitive, even with multiple table joins, and it's here that ORM is doing most of it's stuff. I'm glad I don't have to change a swathe of INSERT/UPDATE/SELECT queries everytime a new property / column is required.
@Richard: How do maintain that balance between letting ORM write the SQL and yet, take over the SQL from ORM? I have several queires that ORM would blow up on if it tried to write them.
@gary: I let ORM handle entities and I've never found the need for exotic SQL to return an entity or an array of entities. Where the SQL gets funky is when you need a recordset with correlated sub-queries and aggregation functions for example. That's not even a job for ORM which as far as I know doesn't return records sets. I just use cfquery as normal in a service method or wherever.
Thank you Richard. This entire post gives me a lot to think about.