Wednesday, June 1, 2011

Installing Rails From Behind A Corporate Proxy

I had a little extra time on my hands and wanted to experiment with Ruby on Rails. The articles that I had read raved about how easy it was to set up the development environment using RubyGems.

Latest versions of Ruby and RubyGems were downloaded and installed without incident. Where I ran into a big problem was installing Rails and its dependencies. Initially I tried to run "gem install rails" but the operation failed due to proxy authentication issues, and this is a well known limitation.

I thought that I would manually download and install the gems but this would have taken hours. As usual, a StackOverflow post provided the inspiration to use CNTLM.

Initially I couldn't get CNTLM to authenticate properly because I didn't know the correct authentication type. The CNTLM manual indicated that there is a switch ("-M") that will test various authentication types using "magic NTLM dialect detection" (!). Using "cntlm -M http://www.google.com" revealed the correct mode that I should use. After making the change in cntlm.ini and restarting the CNTLM service I was able to successfully install rails using the following command:

gem install rails --http-proxy http://localhost:3128

Here's a sampling of the errors that I experienced as well as a successful download:

C:\Ruby192\bin>gem install rails --http-proxy http://localhost:3128
C:\Ruby192\bin>gem install rails
ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    too many connection resets (http://rubygems.org/gems/abstract-1.0.0.gem)

C:\Ruby192\bin>gem install rails --http-proxy http://localhost:3128
ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    bad response Not Found 404 (http://rubygems.org/gems/abstract-1.0.0.gem)

C:\Ruby192\bin>gem install rails --http-proxy http://localhost:3128
ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    bad response Proxy Authorization Required 407 (http://rubygems.org/gems/abstract-1.0.0.gem)

C:\Ruby192\bin>gem install rails --http-proxy http://localhost:3128
Fetching: abstract-1.0.0.gem (100%)
WARNING: abstract-1.0.0 has an invalid nil value for @cert_chain
Fetching: erubis-2.6.6.gem (100%)
Fetching: actionpack-3.0.7.gem (100%)
Fetching: arel-2.0.10.gem (100%)
Fetching: activerecord-3.0.7.gem (100%)
Fetching: activeresource-3.0.7.gem (100%)
Fetching: mime-types-1.16.gem (100%)
Fetching: polyglot-0.3.1.gem (100%)
Fetching: treetop-1.4.9.gem (100%)
Fetching: mail-2.2.19.gem (100%)
Fetching: actionmailer-3.0.7.gem (100%)
Fetching: thor-0.14.6.gem (100%)
Fetching: railties-3.0.7.gem (100%)
Fetching: rails-3.0.7.gem (100%)
Successfully installed abstract-1.0.0
Successfully installed erubis-2.6.6
Successfully installed actionpack-3.0.7
Successfully installed arel-2.0.10
Successfully installed activerecord-3.0.7
Successfully installed activeresource-3.0.7
Successfully installed mime-types-1.16
Successfully installed polyglot-0.3.1
Successfully installed treetop-1.4.9
Successfully installed mail-2.2.19
Successfully installed actionmailer-3.0.7
Successfully installed thor-0.14.6
Successfully installed railties-3.0.7
Successfully installed rails-3.0.7
14 gems installed
Installing ri documentation for abstract-1.0.0...
Installing ri documentation for erubis-2.6.6...
Installing ri documentation for actionpack-3.0.7...
Installing ri documentation for arel-2.0.10...
Installing ri documentation for activerecord-3.0.7...
Installing ri documentation for activeresource-3.0.7...
Installing ri documentation for mime-types-1.16...
Installing ri documentation for polyglot-0.3.1...
Installing ri documentation for treetop-1.4.9...
Installing ri documentation for mail-2.2.19...
Installing ri documentation for actionmailer-3.0.7...
Installing ri documentation for thor-0.14.6...
Installing ri documentation for railties-3.0.7...
Installing ri documentation for rails-3.0.7...
Installing RDoc documentation for abstract-1.0.0...
Installing RDoc documentation for erubis-2.6.6...
Installing RDoc documentation for actionpack-3.0.7...
Installing RDoc documentation for arel-2.0.10...
Installing RDoc documentation for activerecord-3.0.7...
Installing RDoc documentation for activeresource-3.0.7...
Installing RDoc documentation for mime-types-1.16...
Installing RDoc documentation for polyglot-0.3.1...
Installing RDoc documentation for treetop-1.4.9...
Installing RDoc documentation for mail-2.2.19...
Installing RDoc documentation for actionmailer-3.0.7...
Installing RDoc documentation for thor-0.14.6...
Installing RDoc documentation for railties-3.0.7...
Installing RDoc documentation for rails-3.0.7...

Friday, February 18, 2011

A FileNotFoundException, Wrapped In A CommunicationException

For some time I've been involved in an effort to run an existing intranet Windows Forms application in an extranet configuration. It's been quite a struggle because the original app, which I'll refer to as CRAM, was never designed to NOT run in our corporate intranet. The process has required altering firewall rules, server and database configuration, new deployment scripts, and many more things than I care to remember.

A couple of days ago I thought everything was finally finished. One last error to resolve:
System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:04:59.8798782'.
Surely this was just another WCF configuration issue, right? I fired up Wireshark to diagnose the problem. Hmmm, Wireshark showed that a call to a SQL Server 2008 R2 Reporting Services (SSRS) web service was being rejected as 401 Unauthorized. Thinking that authentication was failing, I embarked on a journey of discovery where I learned about SSRS web service changes between the 2005 and 2008 versions. At one point I changed the account under which the SQL Server services run, thinking that the current accounts didn't have the required network access to properly authenticate web service callers. But after this frenzy of learning and tweaking / hacking CRAM still was throwing the same exception.

Lucky for me the original developer had the foresight to log the WCF calls. Using Service Trace Viewer I was able to get a lot more detail about the communication error but not the root cause. Buried in all of this information was another error that I hadn't seen on the client or Event Log:
System.IO.FileNotFoundException: Could not load file or assembly 'Oracle.DataAccess, Version=2.111.6.20, Culture=neutral, PublicKeyToken=89b483f429c47342' or one of its dependencies. The system cannot find the file specified.
Must be a deployment issue, I thought. It didn't seem to be directly related to the communication issue, and it was appearing after the communication errors. I decided to fix the FileNotFoundException to reduce the amount of event tracing data for me to process as well as reassure myself that I wasn't a total failure.

The problem with the Oracle.DataAccess reference is that the required version wasn't present on the application server. I changed the reference's Specific Version property to False, compiled, deployed, and still got the same error. I looked at the installed Oracle client versions on the intranet and extranet application servers and noticed that they were different. I then copied the Oracle.DataAccess.dll (version 2.111.6.20) to CRAM's extranet application server so that it would function as a private assembly. Same result upon running CRAM: FileNotFoundException. Recalling a previous project where I needed to deploy Oracle.DataAccess as a private assembly I copied the same set of dll's to CRAM's executable directory.

And that was the answer. CRAM required a version of Oracle.DataAccess that was unavailable on the application server. Apparently this caused an exception that set off a chain of events resulting in the System.ServiceModel.CommunicationException that vexed me for so long. What I've learned from this- and I probably should have already known this- is that when troubleshooting WCF errors you can't only look at the error on the client: the server must also be taken into account as well. The sheer disconnected nature of WCF means that one might not be getting the complete set of information if only one side of the client + server equation is examined.

Further reading:
Copying Oracle.DataAccess as a private assembly
SSRS configuration changes

Thursday, December 23, 2010

C# language specification installed with Visual Studio

Did you know that a copy of the C# language specification is installed with Visual Studio 2005-2010? The specification is in Microsoft Word format and is located in the VC#\Specifications\1033 directory under the Visual Studio installation directory.

Thursday, October 7, 2010

Use symbolic links to bridge the x86 / x64 development chasm

My main development machine is x86 but the developers who report to me are all running x64. (I haven't had time to rebuild my machine yet.) We're working on an internal administrative tool developed in Silverlight. We've recently started using a third party component that lives in the "Program Files (x86)" directory, which I don't have. The app won't compile for me. Oh, what to do?!?!

Subterfuge! (In the form of a symbolic link.)

mklink /D "Program Files (x86)" "C:\Program Files"

App now compiles. Problem solved.

Monday, October 4, 2010

Crash gracefully

I'm currently in the thick of managing and building the next release of our proprietary internal line-of-business application which I'll call LOBAPP. It's always interesting to see what problems are discovered once acceptance testing begins.

It's challenging to maintain multiple development and test environments, and the personnel that keep everything running aren't under my control- they are motivated mainly because I'm such a nice guy. As LOBAPP has grown, it has unfortunately developed a few 'uninsulated' dependencies. By uninsulated I mean that the dependencies aren't hidden away behind a facade or a dependency injection container where their absence won't destabilize LOBAPP. Yes, I know, bad design, etc., but this is real life and we are dealing with it incrementally.

The point I want to make, though, is about what happens when certain external dependencies aren't available: LOBAPP displays a really ugly error report to the user then closes. Ugh. The app is dying violently instead of gracefully and with dignity.

Unless the exception is completely unhandled, you should make every attempt to tell the user what is happening. You don't need to display to them all of the gory details- in fact, this could be a major security risk- but the user interface should inform the user that something has gone wrong and that someone is being notified of the problem. Their work has already been interrupted so why aggravate and antagonize them more by abruptly crashing with only a witches' brew of technical mumbo jumbo as a farewell from the dying application?

Thursday, September 2, 2010

Debugging Visual Basic's UnhandledException event

One of our applications is comprised of numerous C# projects and a single Visual Basic project. The VB project is the entry point into the application. If the legend is true VB was chosen because of the friendliness of the WindowsFormsApplicationBase class. We've got a global exception handler hooked up to the UnhandledException event. This works well except when you want to debug the global exception handler. The "Visual Basic compiler prevents applications that are built for debugging from raising this event, to enable a debugger to handle the unhandled exceptions. This means that if you are testing your application by running it under the Visual Studio Integrated Development Environment debugger, your UnhandledException event handler will not be called." That's a problem.

Stackoverflow gave me an idea that worked out well in my case. I added code to the WindowsFormsApplicationBase.Startup event handler that- if DEBUG is defined and the debugger is attached- adds a handler for the AppDomain.UnhandledException event. (The conditional logic might be overly defensive but I can't take any chances with this mature, stable application.) When the AppDomain.UnhandledException event handler fires, it merely calls the WindowsFormsApplicationBase.UnhandledException event handler, where I've set a breakpoint.

        Private Sub AppDomainUnhandledException(ByVal sender As ObjectByVal args As UnhandledExceptionEventArgs)
            Dim ex As Exception = DirectCast(args.ExceptionObject, Exception)
            Dim newArgs As New Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs(False, ex)
 
            MyApplication_UnhandledException(sender, newArgs)
        End Sub

Now I can step through my changes. Yippee!

Tuesday, August 31, 2010

404 errors and the XamWebSpellChecker dictionary file

I added Infragistics' XamWebSpellChecker control to one of my Silverlight 4 data entry user controls and it worked like a charm.

Well, it worked on my machine.

Once I got the application on a server spell checking stopped working: a progress dialog would briefly appear and nothing else. Fiddler revealed that when my app was trying to check spelling and requesting the download of a dictionary it was only receiving a 404 response. First I checked the server to make sure that the file had been properly deployed to the IIS6 host. Check. I then reviewed the instructions regarding the usage of the dictionary file and it appeared that I had used the correct file. I started thinking about the binary dictionary file, which had a ".dict" extension. How would IIS know what to do with a .dict file? I logged on to the server and added a new entry: Files with a .dict extension are of "application/octet-stream" MIME type. I issued an iisreset command.

Then with great trepidation I tried to check spelling again...

BAM! Adding the MIME entry was the solution. The spell checker control was functional again. I could rest easy, knowing that no misspelled words would be allowed ever again!

At least not on a single data entry form in a departmental content management system. You've got to start somewhere.