dans.blog


The miscellaneous ramblings and thoughts of Dan G. Switzer, II

SVN reporting "Working copy text base is corrupt"

This morning started off to a rough start. I came in to realize the commit of a merge I made at the end of the day yesterday had failed. Whenever I would attempt to commit the merge changes, it would go through the entire process and then finally fail with a error message of:

org.tigris.subversion.javahl.ClientException: Working copy text base is corrupt

After trying a "Cleanup" and several other steps to rectify the problem, I finally did hit Google to try and find a solution. I came across chris' subversion checksum mismatch - easy workaround which offered several various solutions to the problem.

I tried several of the solutions, but wasn't haven't much success until I came across Michael Sparer's comment:

Thanks for the explanation, which shed some light on how svn manages its working copy. My problem seemed to stem from a bogus file under .svn/text-base, which didn't match the file actually on the server.
- server file (.../x.java): OK
- orig copy from server (.svn/text-base/x.java.svn-base): BOGUS (not same as server)
- checksum (in .svn/entries): matches server, but not server copy

If I can make the bogus copy match what's on the server, then it will also match the checksum, and everyone will be happy.
What I did (i'll call the working dir with the corrupt file "orig-dir":
1. Fresh checkout of svn dir matching orig-dir into /tmp/blah
2. Copy /tmp/blah/.svn/text-base/x.java.svn-base into orig-dir/.svn/text-base
3. Check in successfully

I wish svn would let you refresh a given file from the server...maybe there's a command and I just haven't found it or my svn is too old.

In the end, this ended up being my problem as well. The copy of me .svn/text-base/filename.ext.svn-base was out of sync with the actually copy on the server.

To resolve this, I checked out a clean copy from the server to a tmp folder, then I just replaced the copy in my working folder. After doing this, I was able to check in the file without incident.

NOTE:
I also noticed I had a copy of the template in the .svn/tmp/text-base/ folder. I made a copy of this file and then removed the file this directory before committing. I'm not sure if this step is necessary, but I wanted my local working copy to mirror as closely as possible a fresh working copy.
I could have just wiped my local copy altogether, but I wanted to know the root problem incase it every happens again. Since I have a pretty large repository it takes a while to checkout from SVN, so at least know I have an option to try if I ever run into the issue again that won't require me checking out the entire working directory structure.


Using MSSQL to output time in days, hours and minutes

I was working on some code today where I wanted to output a time span in the format 1d 4h 36m (i.e. "1 day, 4 hours and 36 minutes.") I wanted a pure SQL solution so that I didn't have to worry about formatting later. Here's some example SQL that will format a time span by days, hours and minutes.

declare @startTime datetime
set @startTime = '2009-07-11 14:19:40.000'

declare @endTime datetime
set @endTime = getUtcDate()

select
    convert(varchar(40), dateDiff(mi, @startTime, @endTime)/(24*60)) + 'd '
  + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60) + 'h '
  + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm' as Format1
  , case
      when (((dateDiff(mi, @startTime, @endTime)/(24*60))) > 0) then
          convert(varchar(40), dateDiff(mi, @startTime, @endTime)/(24*60)) + 'd '
        + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60) + 'h '
        + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm'
      when (((dateDiff(mi, @startTime, @endTime)%(24*60)/60)) > 0) then
          convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60) + 'h '
        + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm'
      else
          convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm'
    end as Format2
  , convert(varchar(40), dateDiff(mi, @startTime, @endTime)/(24*60)) + ':'
  + right('00' + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60), 2) + ':'
  + right('00' + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60), 2) as Format3
NOTE:
Make sure to keep the dateDiff() time in minutes. If you switch to hours (or days) you'll have rounding issues you'll have to work around. By sticking to using minutes for the equations, you avoid the rounding issues.

When you run this query in Microsoft SQL Server, you'll see that it outputs 3 columns—Format1, Format2 and Format3.

Format1 always outputs the time span in format 0d 0h 0m, even when the days or hours are 0. In my case, I really wanted to ignore days if no days have passed and hours if we're still under 60 minutes. This lead me to create Format2.

Format2 still uses the 0d 0h 0m format, but it will drop off days if less than 24 hours old and drop hours if less than 60 minutes has passed. This leaves you strings like "6d 4h 52m", "4h 10m" or "3m". In my case, this was the best formatting for my uses.

Format3 is an alternative format that places the time span in the format d:hh:mm. While I'm not using that formatting currently, some people may find it useful. This also shows off how you can force the hours and minutes to output as a digit value (i.e. 0:02:01 = 0 days, 2 hours and 1 minute.)

Hopefully this will prove useful to some of you.


CF9 can protect SQL statements stored as string from SQL injection

One of the many new features in ColdFusion 9 is the ability to use every tag within a <cfscript /> block. Developers seem to either love or hate <cfscript /> and I've always falling into the love (well, maybe not love but I'm extremely fond of it.) I just find <cfscript /> a better method for writing business logic. So for me, the addition of being able to access any CF tag via <cfscript /> will be really nice. However, I wanted to point out one specific use case where the addition of being able to script any tag really comes in handy.

One of issue I've run into in the past with ColdFusion is that there's no good way to protect a SQL statement from SQL injections if your SQL is coming from a string variable. In most cases you're going to build dynamic SQL statements within a <cfquery /> block, occasionally I've found situations were this isn't ideal.

In these cases I've wanted to use a UDF to generate a block of SQL for me, because I can easily re-use the function to re-use common blocks of SQL.

The problem with building the statement via a UDF is that when your SQL is generated as a string, there's you can't use <cfqueryparam /> to secure your data (unless you were to save the string to disk, include it in the query and then clean up the temp file.)

This is where CF9's new functionality of being able to script any tag will become handy. ColdFusion 9 introduces a way to bind a token to a SQL parameter. This would allow you to build a SQL statement completely from a string and then bind tokens in the string to parameters.

The following example code comes from John Whish's cfquery in cfml with parameters post:

// create a query
q = new Query();

// don't need to set datasource if using this.datasource in Application.cfc 
q.setDatasource( "cfartgallery" );

// build the SQL statement 
q.setSQL( "select * from Art where artistid = :artistid and issold = :issold" );

// this is the equivalent of cfqueryparam 
q.addParam( name="artistid", value=2, cfsqltype="CF_SQL_INTEGER" );
q.addParam( name="issold", value=1, cfsqltype="CF_SQL_BIT" );

// run the query and get a query object
result = q.execute();

// dump query object
writeDump( result );

As you can see from the code, the setSQL() method takes a string of SQL to execute. You'll also notice two unique tokens in the SQL ":artistid" and ":issold". When you invoke the q.addParam() method, you're replacing the tokens with a binding SQL variable—which protects you from SQL injections.

So, the addition to the new Query() object ends up solving a problem that I've run into in the past to which there's never been a really good solution for in the past.

I've been meaning to post this since the CF9 beta became public, but work kept getting in the way. :)


My lab 16 months after double bilateral TTA…

In February 2008, our black lab (then just 4 years old) was in pretty bad shape. Both of her rear knees had gotten so bad she basically could no longer walk. We decided to proceed with a bilateral TTA (tibial tuberosity advancement) in both her hind legs. It was a tough decision for us, because it's not a cheap procedure and we weren't sure how we'd manage to keep her constrained for the 11-12 week recoup time.

I've blogged several times about how successful the operation was. She's really done excellent and there are no real hints of her knees ever bothering her. She'll occasionally get up slowly, but I can't say that's because of the surgery. What I do know is the dog still loves to run and jump. The knee surgery has not slowed her pursuit of climbing trees one bit, so I know she's doing well.

The other night my wife was playing around w/our new iPhone 3GS that we're trying out and I thought it was a good idea to post a video of her running and playing fetch in our yard. Well the video is not terribly exciting, I thought it might be useful for someone debating on whether or not it's worth the investment of the bilateral TTA procedure. I know in our case w/out the surgery we would have had to put her down—because she was just in too much pain and it was only going to get worse. So, seeing her run around today still thrills me to no end.

I try to play ball w/my labs for at least 30 minutes every day (weather pending.) I've run Nikki (our black lab) pretty hard this year and she's held up great. She'll run for as long as I'm willing to throw the ball and I've seen no side effects from the surgery at all. So here's she is doing her favorite activity in the world—playing fetch:


Stay w/Verizon Wireless or keep my iPhone 3GS?

Ok, so on Friday I decided to pick up an iPhone 3GS to try it out. My Verizon XV6700 really has issues and just is a horrible phone (I can't see the display when outside and also have always had problems hearing the person on the other land if there's any amount of background noise.)

I've been eligible for my "new every two" for almost a year, but Verizon still hasn't put out a phone I'm completely happy with. Every time I think they might have a winner (like the Storm) it fails.

So on Friday I was supposed to play golf, but due to some severe storms in the morning my party cancelled the outing (although I did end up playing a round later by myself.) Since I had the day off and nothing to really do, I thought I'd see if the local AT&T had any of the iPhone 3GS' in stock. Sure enough they had several still in stock, so w/my 30 day guarantee, I thought I'd sign up and see how it goes.

The biggest thing that's held me back from the iPhone is the lack of physical keyboard and AT&T's network (which I keep hearing bad things about.)

So, over the weekend I did a lot of playing around w/the iPhone. Well there's a lot of stuff I really like, I'm still a bit wary about AT&T's network and I really dislike the software keyboard—especially since the iPhone seems to require a lot of password typing (I'm sure the whole App Store password re-entry is for security purposes, but when you have a very secure password it's very cumbersome to keep re-typing the password in. Instead of constantly re-typing the password I'd rather be able to disable iPhone access to the App Store via the Preferences in iTunes.)

Anyway, is AT&T's network really that bad? So far the coverage has seemed ok, although when playing golf Saturday while my bars showed full, the conversation with my wife seemed to be breaking up a lot.

Should I go ahead and move the wife and me to iPhones and move off VZW?


structFilter() UDF - Filtering a structure based upon a regular expression

Today I thought I'd share a little ColdFusion helper function I've been using for years: structFilter(). The structFilter() parses a structure and returns only the keys that match a given regular expression.

<!---
 Searches a structure for keys matching a regex and returns a structure containing the
 matched keys.

 @param input        The structure to filter. (struct; required)
 @param regex        The regular expression to filter keys against. (string; required)
 @param useNoCase    Do a case insensitive search? (boolean; default = true)
 @param useDeepCopy  Do a deep copy on the keys? (boolean; default = false)

 @return Returns a structure containing the matching keys.
 @author Dan G. Switzer, II (dan.switzer@givainc.com)
 @version 1, June 17, 2009
--->
<cffunction name="structFilter" returntype="struct" output="false" access="public"
    hint="Filters keys in a structure to those matching the regular expression.">
  <!---// define valid arguments //--->
  <cfargument name="input" type="struct" required="true" />
  <cfargument name="regex" type="string" required="true" />
  <cfargument name="useNoCase" type="boolean" required="false" default="true" />
  <cfargument name="useDeepCopy" type="boolean" required="false" default="false" />

  <!---// define local variables //--->
  <cfset var result = structNew() />

  <!---// if using NoCase and the (?i) directive doesn't exist, append to regex //--->
  <cfif arguments.useNoCase and not reFind("^\(\?[mx]*i[mx]*\)", arguments.regex)>
    <cfset arguments.regex = "(?i)" & arguments.regex />
  </cfif>

  <cfloop item="key" collection="#arguments.input#">
    <cfif reFind(arguments.regex, key)>
      <cfif arguments.useDeepCopy>
        <cfset result[key] = duplicate(arguments.input[key]) />
      <cfelse>
        <cfset result[key] = arguments.input[key] />
      </cfif>
    </cfif>
  </cfloop>

  <!---// return the new structure //--->
  <cfreturn result />
</cffunction>

I've found this helper UDF to be extremely handy in when you have a bunch of serialized form fields that follow a given nomenclature. For example, if you have a bunch of form fields like:

<input type="text" name="name" value="" />
<input type="text" name="description" value="" />

<!---// dynamic list of variables //--->
<cfloop index="i" from="1" to="10">
  <cfoutput>
    <input type="text" name="id_#i#" value="" />
  </cfoutput>
</cfloop>

Using the structFilter() UDF you can vary easily process all the fields starting with "id_" by doing:

<!---//
  get all the fields:
  * starting with "id_"
  * and ending with one or more numbers
//--->
<cfset stIds = structFilter(form, "^id_\d+$") />

This will give you a structure containing all the "id_" fields that you can then process in a collection-based loop. This method is much more effective then passing in some kind of hidden "counter" variable where you loop from 1 to the counter variable (which is a technique I've used in the past) as it doesn't rely on the numbers being serialized.

There are obviously many other uses for this function, but I use it a lot for processing complex forms that have dynamically generated fields that have a fixed nomenclature.


Safari 4 z-index issue with Flash

I was doing some testing with Safari 4 (on Windows Vista) and noticed that it was running into the z-index issue that's normally fixed by having a wmode attribute. The issue is that Flash was always displaying on top of all other elements. Normally this can be fixed by applying the wmode attribute of either "opaque" or "transparent" to your SWF, however that was already present.

After playing around with the problem for a good bit trying various solutions of applying an z-index to elements, I decided to check to see if I had the latest version of Flash installed. Turns out I was running Flash 10,0,22,54 and 10,0,22,87 is the latest version. Sure enough, after upgrading Flash everything started working as expected.

So, if you're having problems with Flash being overlaid over all of your elements in Safari, try upgrading to the latest version of Flash to see if it fixes the problem.

(To see how things should work, check out http://pipwerks.com/lab/swfobject/z-index/2.0/dynamic.html. If mousing over the menu options is not showing some pop-up elements, then something has gone awry—so try upgrading Flash.)


CFHTTP "Connection Failures" issues when using mod_rewrite

Several years ago I ran into some issues with CFHTTP giving "Connection Failures" when using GZIP, but recently I ran into some new "Connection Failures" when using CFHTTP. I recently installed some mod_rewrite rules on our server to:

  • Redirect naked domains to the www subdomain (i.e. map domain.com to www.domain.com)
  • Force SSL

My rules were pretty simple and worked great when invoked from the browser, but I quickly realized they were causing issues with CFHTTP.

more…


Freeing up disk space on your C: drive

I've been fighting a bit of a battle with my development server. When I originally set up the box, I created the C: partition with a relatively small size based on some performance guidelines I had read. While I had plenty of space when I built the server, the ever growing Windows folder has eventually eaten up all the disc space. The biggest culprit of this being the C:\Windows\installer folder—which gets larger each time Windows does an update.

Today I finally had no choice but to do something. There aren't a lot of choices when a system drive is running out of space, you basically can:

  • Remove unused files—been there, done that; nothing else to remove
  • Backup and reformat—which is way too big of pain to do
  • Use a tool to resize the partition (which only works if your drive has been partitioned into multiple logic drives)
  • Move files to a different drive and create a symbolic link (junction) to the folders

I thought about going the resizing route, but there's some risk involved and I wanted to minimize the time I was working on this. So I decided to go the junction route.

Since the C:\Windows\installer folder was without a doubt the folder eating up the majority of disk space for me, I decided to move this folder to another drive. However, there are some caveats of moving the C:\Windows\installer folder where if you don't get the permissions just right, then Windows will delete the junction and the folder it points to—where leaves you screwed.

Fortunately for all of us, Simon Bailey has written a nice batch file you can use to move C:\Windows\installer folder for you. If you're not interested in the batch file, you can also read his detailed blog post on the steps for freeing up space on your C: drive.

Now that I've freed up gigabytes of data from my C: drive, my server is happy again—which makes me happy.


Marquee jQuery Plug-in Released!

My current employer (Giva, Inc) has released another jQuery plug-in today called the Marquee jQuery Plug-in. The jQuery Marquee plug-in converts a list element (<ul /> or <ol />) into an ESPN-style scrolling marquee. Messages are scrolled in from top or bottom (based on the yScroll option) and longer messages will then ticker to the left in order to show the full message.

The Marquee jQuery Plug-in has an example you can look at or you can see several different marquees in different configurations on the Giva Labs - Marquee Example Page.


Trip to Denver

On Monday the wife and I just got back from a short trip out to Denver. We flew out last Thursday to surprise my cousin for his high school graduation. While we weren't able to make it out for the actual ceremony, we had it worked out to surprise him at the restaurant for dinner.

I had worked out plans with my aunt to keep things as a complete surprise. When he was standing in line, I snuck up behind him patted him on the shoulder and said "I hear congratulations are in order!" The look on his face was priceless when he turned around. You could see the confusion of "Who is this person that looks like my cousin? That can't be him. Why would he be here in Denver?" A true classic. He was speechless for like the first 10 minutes.

It was a great trip. I've been out to Denver a number of times in the past 25 years, but this was my wife's first trip. In the 5 days we were there we put on 700 miles on the rental car—but those miles pretty much all came on Friday, Saturday and Sunday. We drove to Boulder a couple of times, Breckenridge, Estes Park and just explored Denver a bit.

On Saturday, we drove up to Boulder and had lunch at the Walnut Brewing Company—and they had one of the best fruit salads I've had in a long time. The fruit salad was mixed with a combination of cinnamon, honey, lemon juice and sour cream—it was really quite tasty.

After lunch, we drove back to Denver for my cousin's graduation party. My cousin is considerable younger than I am (19 years younger,) so there were always a ton of pictures of the two of us where I'm holding up. Anyway, since he's 6'1" and growing, I figured I better take one last picture before I can't pick him up anymore. :)

It's so hard for me to believe the baby I remember feeding and playing with is now on his way to college to play Hockey.

On Sunday we went up to the Rocky Mountain National Park. While the park isn't completely opened up yet, we were able to make it up to 12,000 ft—so we had a decent view (we couldn't quite make it to the gift shop, the road was closed right before the last leg.) I knew the road would most likely be closed since it wasn't scheduled to be opened up until May 22, but I kept hoping we could make it to the Continental Divide. Alas, it just gives us something to do next time we're in town.

Anyway, here are a couple of panoramic photos I took and stitched together with Windows Live Photo Gallery—which does a wonderful job of stitching photos together.

rocky_pan_2

rocky_pan_3

It was a great trip and I'm so glad we were able to make it out to see my cousin.


Logging in to Vista using an administrative share

I've recently migrated two of my PCs to Vista. I was trying to wait until Windows 7, but I had to physically replace the boxes so my hand was forced into (yeah, I could have downgraded, but I figured I should actually work with Vista a bit before moving to Windows 7 if for no other reason that to appreciate it more.)

Anyway, one of the problems I've had was logging into the boxes using the administrative shares. Well this is disabled by default (and for good reason,) I needed a way to access via the shares and came across this article from Microsoft:

Error message when you try to access an administrative share on a Windows Vista-based computer from another Windows Vista-based computer that is a member of a workgroup: "Logon unsuccessful: Windows is unable to log you on"

This has allowed me to access my box from Windows XP and my other Vista boxes when I log in w/admin credentials—which is exactly what I wanted to do. While this isn't a recommended thing to enable, it seems you can toggle it off/on without rebooting—which is nice.


Pagination in MSSQL 2005 with one-to-many joins

I was working on restructuring some old code that needed some pagination. The query in question used a one-to-many join the required information together. Imagine a search engine where you're wanting to search over orders, but want to group the results by customer. The output might look something like:

Gary Dell'Abate
  Order #: 12098
  Order #: 13232
  Order #: 14551
  Order #: 16770
Fred Norris
  Order #: 11021
  Order #: 11029
Robin Quivers
  Order #: 10010
  Order #: 11001
  Order #: 12001
Howard Stern
  Order #: 13001

So, in my situation I want to also paginate by the customers. When SQL Server 2005 was introduced, it added a new feature called Common Table Expressions (CTEs.) One of the most useful features of CTEs is to paginate recordsets. Typically when paginating results, you will use the row_number() function—which creates a new unique row number for each row in your recordset:

more…


Better handling of winmail.dat messages in Thunderbird with LookOut

Over the last few days I've been setting up a new laptop and got Thunderbird up and running (which I'm now going to try to use exclusively.) I've been using Thunderbird on my laptop for work related e-mails for 4 or 5 years now. One of the issues I've always had with it was handling TNEF encoded messages (aka "winmail.dat") that Outlook insists on sending.

Now this is really a problem with Outlook in that it doesn't always honor the "HTML" format and sometime insists on sending e-mail in Outlook's native format. If you use Outlook, you have no problems. However, every other client will just get the dreaded "winmail.dat" file as an attachment.

In the past I've just used program (like Winmail Reader) to open the winmail.dat file and view the RTF and attachments. However, I decided to search the Thunderbird Add-ons page to see if anyone had developed a better solution and thankfully Aron Rubin has developed the wonderful LookOut add-on.

LookOut automatically converts the winmail.dat into it's associated attachments and creates a RTF file that you can double-click on to open in Word (or your associated RTF application.) This solution works really well, because I know longer have to open the winmail.dat in an external program just to see the attachments.

So kudos to Aron Rubin for this excellent add-on!


Targeting specific Browsers with CSS

Pail Irish posted a nice concise list of CSS hacks for targeting specific browsers. While I'd love to avoid using hacks altogether, I just continually find cases where it's necessary so this list is a handy little reference:

/***** Selector Hacks ******/

/* IE 6 and below */
* html #uno  { color: red }

/* IE 7 and below */
*:first-child+html #dos { color: red }

/* IE 7 and modern browsers */
html>body #tres { color: red }

/* Modern browsers (not IE 7) */
html>/**/body #cuatro { color: red }

/* Opera 9.27 and below */
html:first-child #cinco { color: red }

/* Safari */
html[xmlns*=""] body:last-child #seis { color: red }

/*safari 3+, chrome 1+, opera9+, ff 3.5+ */
body:nth-of-type(1) #siete { color: red }

/* safari 3+, chrome 1+, opera9+, ff 3.5+ */
body:first-of-type #ocho {  color: red }

/* saf3, chrome1+ */
@media screen and (-webkit-min-device-pixel-ratio:0) {
 #diez  { background: #FFDECE; border: 2px solid #ff0000  }
}

/***** Attribute Hacks ******/

/* ie6 and below */
#once { _color:blue }

/* ie7 and below */
#doce { *color: blue } /* or #color:blue */

/* 'Modern Browsers' includes IE8, whether you agree or not.. :) */

I did notice that the "*" hack (i.e. "*color: blue") also seems to get picked up by IE8, while he lists the hack as for being for IE7 and below.

Paul even set up a test page with all the hacks for testing.

Nice job Paul!