Reset Access Datasources Without Resetting ColdFusion

I am “stuck” with a few web applications that provide web front-ends to Access databases. I am not the owner of these databases, and for various reasons, the database owner will not upgrade it to SQL Server. Alas, this sometimes causes headaches like the following.

The Access databases are on a network fileserver. Occasionally, this server goes down or resets, and ColdFusion’s datasource connection to the database gets stuck in a disrupted state. I’ve come to learn two things you can try to get ColdFusion’s connection to the Access datasource working again without resetting the entire ColdFusion Application Server service, which is obviously disruptive to your entire site and all of its applications.

  1. Reset the ODBC services. The ColdFusion ODBC services (such as “ColdFusion 8 ODBC Agent” and “ColdFusion 8 ODBC Server”) actually handle the ODBC connections to DataDirect driver databases such as Microsoft Access databases. This is handy. Just reset these services and often connectivity is restored.
  2. Use the “Disable Connections” option in the datasource configuration. If you are using JDBC drivers (for instance, perhaps to connect to Microsoft Access 2007 databases), resetting the ColdFusion 8 ODBC Server service won’t help. Darn. However, if you go into the datasource’s configuration in ColdFusion Administrator, you can check the “Disable Connections” checkbox and submit the change. This will force ColdFusion to disconnect from the database. Immediately go back in and uncheck the checkbox and submit again. Your datasource will now have connectivity reestablished without a ColdFusion restart!

Happy days! The second option won’t be helpful to you if you have many, many datasources. However, this is better than a ColdFusion reset that would disrupt all of your apps.

Generating a Decrypted Memento for a Transfer Decorator

In my last blog post, I discussed how to efficiently handle multiple encrypted fields while using Transfer. The key was using onMissingMethod in the Transfer decorator. However, what about when we use the memento with Transfer's getMemento() method? This approach can be especially handy to quickly transfer an object's properties when using web services to Flex or AJAX front ends.

For reference, here was the object definition we were using in our sample Transfer configuration.

XML:
  1. <object name="Reg" table="Regs" decorator="MyProj.com.Reg">
  2.   <id name="ID" column="RegID" type="numeric" />
  3.   <property name="FName" type="string" column="RegFName" />
  4.   <property name="LName" type="string" column="RegLName" />
  5.   <property name="LastMod" type="date" column="RegLastMod" />
  6.   <property name="Expires" type="date" column="RegExpires" nullable="true" />
  7.   <property name="EncHome" type="string" column="regHome" />
  8.   <property name="EncWork" type="string" column="regWork" />
  9.   <property name="EncMobile" type="string" column="regMobile" />
  10.   <property name="EncSMS" type="string" column="regSMS" />
  11. </object>

The process is very simple. Recall that we prefixed our Transfer properties with "Enc" to know which fields are encrypted. Let's create a getDecryptedMemento() method. It will loop through all of the keys in the memento, which is just a struct, and decrypt any of the encrypted fields, saving them as new keys in the struct (i.e. "EncHome" is decrypted as "Home" in the struct).

Something like this will do the trick:

CFM:
  1. <cffunction name="getDecryptedMemento" access="public" returntype="struct" output="false">
  2.   <cfset var memento=getMemento()>
  3.   <cfset var key="">
  4.   <cfset var newkey="">
  5.   <cfloop index="key" list="#StructKeyList(memento)#">
  6.     <cfif Left(key,3) is "Enc">
  7.       <cfset newkey=RemoveChars(key,1,3)>
  8.       <cfset memento[newkey]=decryptValue(memento[key])>
  9.     </cfif>
  10.   </cfloop>
  11.   <cfreturn memento>
  12. </cffunction>

Not much to it. Note that we would use the same decryption function as we are using in onMissingMethod for the dynamic accessors. I'm calling it decryptValue() for this sample.

In the end, our getDecryptedMemento() method returns a memento that looks just like the Transfer-generated getMemento() output, with decrypted versions of the encrypted fields!

Handling Multiple Encrypted Fields in a Transfer Decorator

I had a recent project that had multiple fields in a particular table that needed to be encrypted for privacy and security considerations. I was using Transfer for the project. The typical way to handle the encryption is to create custom accessor methods in a decorator component. The Transfer wiki has a decent explanation of this concept on a page entitled How to Encrypt User Passwords Using a Decorator. However, it is intentionally simple, demonstrating a single property. Multiply your getter/setter methods by how many properties you will be working with, and the lines of code can add up. This framework stuff is supposed to prevent that kind of repetition! Well, utilizing onMissingMethod() and the core concept on the Transfer decorator example, we can handle this scenario efficiently.

First, let's consider the object definition in our sample Transfer configuration file.

XML:
  1. <object name="Reg" table="Regs" decorator="MyProj.com.Reg">
  2.   <id name="ID" column="RegID" type="numeric" />
  3.   <property name="FName" type="string" column="RegFName" />
  4.   <property name="LName" type="string" column="RegLName" />
  5.   <property name="LastMod" type="date" column="RegLastMod" />
  6.   <property name="Expires" type="date" column="RegExpires" nullable="true" />
  7.   <property name="EncHome" type="string" column="regHome" />
  8.   <property name="EncWork" type="string" column="regWork" />
  9.   <property name="EncMobile" type="string" column="regMobile" />
  10.   <property name="EncSMS" type="string" column="regSMS" />
  11. </object>

Note that I have prefixed the Transfer properties with "Enc". This isn't required, but will simplify our processing later on. So in this sample, we have 4 properties: Home, Work, Mobile, and SMS. The concept is that these are client phone numbers that we want to protect with encryption. So, Transfer will generate getEncHome(), getEncWork(), getEncMobile(), getEncSMS(), and corresponding setters. However, we want to provide getHome(), getWork(), getMobile(), getSMS(), and corresponding setters, that get and set the properties after encryption/decryption.

Use onMissingMethod()

To accomplish this, we might have an onMissingMethod() function in the decorator like this:

CFM:
  1. <cffunction name="onMissingMethod" access="public" returntype="any" output="false">
  2.   <cfargument name="missingMethodName" type="string" required="true">
  3.   <cfargument name="missingMethodArguments" type="struct" required="true">
  4.   <cfscript>
  5.     var func="";
  6.     var encList="Home,Mobile,SMS,Work";
  7.     var method=Arguments.missingMethodName;
  8.     var args=Arguments.missingMethodArguments;
  9.     var firstArg=args[ListFirst(StructKeyList(args))];
  10.     var prop=RemoveChars(method,1,3);
  11.     // Handle encryption/decryption of encrypted values:
  12.     if( ListFindNoCase("get,set",Left(method,3)) and
  13.         ListFindNoCase(encList,prop) )
  14.     {
  15.       func=variables[Insert("Enc",method,3)];
  16.       if( Left(method,3) is "get")
  17.         return decryptValue(func());
  18.       else
  19.         func(encryptValue(firstArg));
  20.     }
  21.   </cfscript>
  22. </cffunction>

As a good security measure, we use an "encList" string to check that only proper methods can be called. Then we examine the method name. If it starts with "get" or "set" and ends with one of the list items, we proceed. Thus, we'll process a method like getHome(), but not a method like getFoo(). We then call getEncHome() and decrypt it for getters, encrypt and call setEncHome() for setters.

Dynamic Method Calling

But alas, how do we do this dynamically without a long if-then-else chain? We call the proper method by referencing it from the component's variables scope. Terrence Ryan describes this in his blog post Cheap and Easy Dynamic Method Calling in CFScript. We construct the method name, in our case, by inserting "Enc" after the "get" or "set" prefix of the method name passed into onMissingMethod (thus "getHome" becomes the Transfer method "getEncHome"). Assign the function to the local "func" variable, then call it as if "func" was the function. Finally, handle any encryption or decryption that you will be doing. For this sample, I have encryptValue() and decryptValue() methods handling the details of that work.

Summary

We've used two solutions to handle multiple encrypted fields in the decorator. First, we used onMissingMethod() to handle processing of all of the properties in a single method. Then, we used dynamic method calling to call the Transfer-generated accessors within onMissingMethod().

The end result is being able to use setHome(), getHome(), etc. with the encryption and decryption being handled automatically. Great!

In addition to accessors, we may sometimes want to use the object's memento with the Transfer method getMemento(). In my next blog post, I'll demonstrate how that process can also be done very efficiently.

Separating a Large Repository

A few months ago, I posted an article about combining multiple Subversion repositories into one large repository. Some folks have expressed an interest in doing the opposite--separating one large repository into multiple smaller repositories. The process is not without its quirks, but it can be done.

At first glance, you'd conclude the process would work much the same way: Loop through the individual directories in the large repository, create smaller repositories for each one, then dump and import the contents of each directory into its small repository.

The tricky part is that the Subversion dump command dumps everything in the repository, by revision. In order to pull just a single directory, you must filter a complete dump with the "svndumpfilter" command. This blog post by AllMyBrain.com basically explains how to accomplish this in Linux. I usually have to work on a Windows box on the job, so I wrote up a script to accomplish this in a Windows batch script.

The strategy is the same as the Linux script, though. We're going to use "svnadmin dump" the large repository, then use "svndumpfilter" to filter by just the directory we want, then "svnadmin load" the results into the newly created repository. All of this can be combined into a single statement via piping:

DOS:
  1. svnadmin dump c:\my\large\repo\ |
  2. svndumpfilter include MyDirectory |
  3. svnadmin load MySmallRepo\MyDirectory

This will make a little more sense when we look at the full script. Let's just put it out there and then go through it.

DOS:
  1. SET SmallRepoPath=c:\SmallRepos
  2. SET PathToRepo=c:\BigRepo
  3. SET UNCToRepo=file:///c:/BigRepo
  4. SET PathToChkout=c:\BigRepoChkout
  5.  
  6. mkdir %PathToChkout%
  7. svn co %uncToRepo% %PathToChkout% --ignore-externals
  8. dir /A:D /B %PathToChkout%> %PathToChkout%\dirs.tmp
  9. for /F %%i in (%PathToChkout%\dirs.tmp) do (
  10.     if not %%i==.svn (
  11.         echo Processing "%%i"...
  12.         mkdir %SmallRepoPath%\%%i
  13.         svnadmin create %SmallRepoPath%\%%i
  14.         svnadmin dump %PathToRepo% | svndumpfilter include %%i | svnadmin load %SmallRepoPath%\%%i
  15.     )
  16. )
  17. del %PathToChkout%\dirs.tmp
  18. rmdir /S /Q %PathToChkout%

First, we're setting our paths. "SmallRepoPath" will be the directory holding all of the small repositories we'll be creating. "PathToRepo" and "UNCToRepo" point to the big repository as DOS and UNC paths, respectively. "PathToChkout" points to a Subversion checkout of the large repository.

First, we check out the large repository with the "svn co" command. We do this just so that we can call the "dir /A:D /B" command, which says, "List just the directories in the checkout directory." We use that output to loop through each directory in the large repository.

Then, for each directory in the large repository, we create a corresponding small repository, then do our dump/filter/load combo. Again, we're dumping the contents of the large repository, using "svndumpfilter" to filter by directory, then loading that filtered dump into the new small repository.

Finally, we just do some cleanup by removing our temp files and the checkout directory.

There are a few caveats with this code.

First, it will import all of the large repository's revisions into the smaller repository. There are svndumpfilter arguments to prevent this, such as --drop-empty-revs and --renumber-revs, but I found the Windows Subversion binaries to be problematic with these arguments. The end result is that you have more revision numbers than needed, but only the relevant data is actually imported into the repository, and viewing logs on just the imported directory will still obviously show revision logs related to that directory, so there's really little harm done.

Second, the dump/filter/load action doesn't always work on a directory that has been moved (copied/deleted) from another location within the large repository. What's worse, it won't fail, it just won't load any data into the small repository. To address this, use the --revision argument on the "svnadmin dump" command to do a dump starting at a revision after the move took place. Doing so will give the "svndumpfilter" command something it can work with.

This process is certainly more complicated to explain, but ultimately there's not that much more going on. Hopefully this explanation is helpful to you.

  Theme Brought to you by Directory Journal and Elegant Directory.