Saturday, March 01, 2003

Flash to be integrated into Japan's i-mode

This news may not make much noise among those not into wireless/mobile development, but I think it could be more imporant than many will realize. Macromedia announced this week that they had reached an agreement with NTT DoCoMo, Japan's dominant player in mobile/wireless, to have Flash embedded on their "i-mode" phones. Who cares? Well, Japan has led the way in mobile development, in particular because DoCoMo's dominance means that i-mode and their network/phones have been pretty much the only game in town, and therefore they didn't suffer the stratification of networks, carriers, phones, etc. that we do in the US. Things just congealed there in a way that we can't yet dream of. We can hope that things may improve here, whether or not i-mode itself ever extends much outside Japan.

So what, then? Well, my excitement is that this is a great notch in Flash's belt to become more widespread in mobile phones, even if only in Japan. And success in Japan has been a harbinger of possible moves to the rest of the world. It's not obvious, but it's not entirely unrealistic. When folks eventually realize the power that they can bring to their web apps with Flash, and further that the same skills and results created for web apps can be applied to to mobile phones, I predict there will eventually be an upswelling of interest and acceptance of the platform on other devices and networks. It will take time, but I think this is a significant announcement--and not just for Flash, nor even just current wireless developers.

Perhaps one day many current web developers will be better able to ply their trade in support of wireless apps. WAP and other HTML-based approaches have come and mostly gone--though not for entirely justifiable reasons in some respects. (As a contributor to Professional WAP, I gave that platform a shot back in 2000.) But Flash on a mobile phone: that just makes a lot of sense, and may cause Flash development to make more sense for web app developers as time, markets, consumers, etc all come together to make it a platform of interest. I could be wrong, but we shall see.

Friday, February 28, 2003

Could not update; currently locked by user 'admin' on machine...

Folks, have you ever run into this error:

Could not update; currently locked by user 'admin' on machine 'machinename'

It occasionally happens with an Access database, in my case when someone's trying to do an insert. I may have found an explanation. Now please, keep the suggestions about moving off of Access to yourselves. I KNOW that it's not intended for production use, yadda, yadda. There are people who otherwise find it perfectly adequate.

Before I offer the explanation, to those who might persist with wanting to say that the solution is to move off of Access, to quote Dr. Evil, talking to his son in Austin Powers:

Evil: "Alright, zip it!"
Scott: "You know you can't even.."
Evil:"..zip it! Zip!"
Scott: "Look all I.."
Evil: "..Ladies and Gentlemen of the jury, ex-zip it A!"
Scott: "Number Two would you please back me up.."
Evil: "Look I'm Zippy Longstockings!"
Scott: "I can't.."
Evil: "..You must zip it! (whip sound) zip it good!"

All I want to discuss is this particular problem. ;-)

The error message, and nearly every reference that I've ever found relating to it on the net, has suggested that the problem is that someone else "has the file open". That may make sense in a local network environment, but I just knew that couldn't be the case here. It's on a commercial host. And the DB is NOT in a web-accessible path, so it's not a matter of someone else downloading it or opening it over the web. And the client doesn't ever download or upload new copies via FTP. It's ONLY updated on the net.

So it's really just a strange error that occurs once in a while (in my case, it could be monthly). I'm betting that quite a few others have experienced this problem and just not been able to resolve it.

After all that lead up, what's the solution? Well, in my case, when I looked at the code in which the error was occurring, I noticed something that just had never caught my eye before. It was doing a CFQUERY inserting against one table, and then right after that doing a CFTRANSACTION with another insert (into a different table but the same datasource) followed by another query doing a SELECT MAX to get the autonumber key that was just created. (Again, let's not debate the veracity and wisdom of using autonumber primary keys, but I will note that there are better ways to get the last inserted primary key, even in Access. See my previous blog entry on this.). FWIW, that CFTRANSACTION was using an ISOLATION value of READ_COMMITTED.

I don't know why it was doing the first query outside the CFTRANSACTION, but it struck me that there may be a conflict where the first CFQUERY might still be being processed when the CFTRANSACTION tried to obtain its needed lock on the DB. That would maybe cause this error.

(Indeed, one might wonder if another scenario could cause the problem. Imagine a template simply doing a CFTRANSACTION (perhaps again important that it's doing a READ_COMMITTED), which simply happens to try to run at the same time that another CFQUERY against that datasource ran at the same time, on another thread. It may be that what we're seeing here is simply Access's version of a lock pending timeout. I mean, the error was occurring on the INSERT inside the transaction (the first query inside it), so while we might expect any such DB lock timeout to happen on the CFTRANSACTION itself, maybe the way CF passes the ODBC SQL commands to the Jet Engine, the error can't be reported on the CFTRANSACTION and therefore it happens on the CFQUERY.)

Anyone have thoughts? Does this sound useful? I've changed the code in this one template to see if the problem will never show up again. I'm not in a position to run some load testing right now so I can't see if I could recreate and prove if this solves it. If anyone can, please do report your findings.

Monday, February 24, 2003

Impact of mappings on use of CFCs in CF, web services, and Flash remoting

If you've got code running outside the regular web root on your server, you may know that there are a couple of mechanisms in CF that allow you to create "mappings" to point to those. But what about when you're trying to invoke a CFC, whether with CFINVOKE/CFOBJECT/CreateObject, as a web service, or via Flash Remoting? The two mapping approaches have different fundamental purposes, and also they don't both work for all these attempts you may make to invoke a CFC. I've provided here a "map" (pardon the pun) of what works with what.

First, for background, the first of the two approaches for creating mappings are the CF Admin, which at its core is intended to allow tags like CFINCLUDE and CFMODULE to find CF templates using a mapping, such as to find files outside the webroot or simply to create an alias that facilitates easier reuse among multiple directories. People often get tripped up trying to use this mapping in a URL, which is not it's purpose. It's solely for internal use by CF tags and functions, so it does indeed work for invoking a CFC via CFINVOKE or CFOBJECT/CreateObject. But it doesn't work for invoking the CFC as a web service or via Flash Remoting.

The second approach is to create a virtual directory mapping using CF's underlying jrun-web.xml file. If this is new to you, check out my previous blog entry on the topic. It's easy to do and has value for allowing URLs that point to code outside the webroot or again to create an alias to hide the real directory name. Since it works with URLs, it DOES work for invoking a CFC via web services, but not with CFINVOKE/CFOBJECT/CreateObject, nor via Flash Remoting.

Sadly, it turns out that Flash Remoting doesn't work with either mapping approach, at least in my testing. So the only way to call a CFC via remoting is for it to be in the webroot. I'd welcome any observations others have for finding another solution for that.

To summarize, here's what I've found about invoking CFCs in different ways and with different mappings:

- cfinvoke/cfobject: admin mappings YES, jrun-web.xml mappings NO
- web service: admin mappings NO, jrun-web.xml mappings YES
- flash remoting: admin mappings NO, jrun-web.xml mappings NO

I have only tested this on a server with the built-in web server, so I haven't tested how an IIS virtual directory mapping might affect things, nor how use of IIS may change the nature in which either of these other two mappings are respected.

How to Stop Requiring the RDS Password for CFC Exploring

Would you be interested in enabling folks to browse your CFCs documentation without needing to know the RDS or Admin password for your server in order to do so? You can.It just takes either of a couple of undocumented steps.

You may know that you can view the documentation for a CFC by browsing it, but Macromedia designed that feature to require you to provide the server's RDS or Administrator password in order to do so. That's unacceptable for some servers, since those passwords can grant too much access in other respects. But you can change things so that either no password is expected at all (which may not be desirable either), or by requiring a password of your choosing that's used just for browsing CFCs, or by requiring some other username/password pair of your choosing.

To allow browsing without any password at all, you can simply rename the Application.cfm file in the directory where the CFC Explorer is stored: \CFusionMX\wwwroot\CFIDE\componentutils. (You don't directly execute code in this directory, but when you browse a CFC it is where CF runs the CFCExplorer on your behalf.) By renaming that directory's Application.cfm, for instance to xApplication.cfm, the code in that directory will no longer expect the RDS or Admin password. (Some have asserted that another solution is to simply disable the RDS password entirely, but that's throwing the baby out with the bathwater, since it will expose your server to being used by features in tools like ColdFusion Studio, HomeSite+, and Dreamweaver MX which use RDS to access databases, files, and more on the server.)

But while renaming the Application.cfm will no longer require any password at all to browse CFC's, some may worry that exposing their CFC's documentation to just anyone will be an equally unacceptable security risk.

Another alternative is to simply replace the Application.cfm with one that does some other form of authentication of your own choosing. Most have written their own security processing in Application.cfm, so feel free to roll your own. If you'd like to use one written just for this purpose, I've provided one here. Feel free to copy and paste it into a new Application.cfm (again, after renaming the current one in the CFIDE\componentutils directory). There's quite a bit of comments, with only about 30 lines of actual code:

<!--- Application.cfm that can be used to replace that in \CFusionMX\wwwroot\CFIDE\componentutils\ so that the developers don't require the Admin or RDS password in order to explore CFCs. Instead, they will be prompted for a password that you can define in this program. See the line below, which expects the password to be "getcfc" by default:

    <cfif form.password is not "getcfc">

You'll want to change that to some password that you prefer to use for browsing CFCs. You could also switch the code to prompt for both a username and password and then do a lookup in a database instead.

To implement this, rename the Application.cfm that's currently in the \CFusionMX\wwwroot\CFIDE\componentutils\ directory, and save this one there.

Then tell your developers to use whatever password you choose to implement for browsing CFCs. This change will have no other effect of the funcitoning of features that leverage the RDS password. It only overrides the password needed for browsing CFCs.

Note also that this is setup to allow logins to last for an hour (which you can change with the IDLETIMEOUT on CFLOGIN) and it supports a logout, which you can remove if you'd like by taking out the first 4 and the last 7 below.
--->


<!--- if they clicked logout button, log them out --->
<cfif isdefined("form.logout")>
    <cflogout>
</cfif>

<cffunction name="dologin">
    Please Login:
    <form method="post">
    Password: <input type="Password" name="password"><br>
    <input type="Submit" value="Login" name="login">
    </form>
</cffunction>

<!--- set login to allow up to an hour of idleness before timing out, and use a unique applicationtoken to as not to conflict with any others--->
<cflogin applicationtoken="cfcexplore" idletimeout="3600">
    <!--- if here, user is not yet logged in. if not responding to username/password prompt, show them login form --->
    <cfif not isdefined("form.Login")>
        <!--- call the dologin function defined above --->
        <cfscript>dologin();</cfscript>
        <cfabort>
    <cfelse>
        <!--- validate username/password here --->

        <cfif form.password is not "getcfc">
            Invalid password. Try again.
            <!--- call the dologin function defined above --->
            <cfscript>dologin();</cfscript>
            <cfabort>
        </cfif>
        <!--- if valid username/password, log them in--->
        <cfloginuser name="cfcexplorer" password="#form.password#" roles="">
    </cfif>
</cflogin>

<!--- -if they're logged in, give them a logout
-getauthuser() returns the empty string if not logged in --->
<cfif getauthuser() is not "">
    <form method="post">
    <input type="submit" name="logout" value="Logout">
    </form>
</cfif>

The comments explain that it creates an expected password of "getcfc", and how you can change it, even to instead use a query to test usernames and passwords if desired. Hope you find it helpful.