Wednesday, April 7, 2010

Watir 102: Javascripting XPCOM to remove session cookies in a FireWatir script

Yesterday (see previous entry) I was playing with FireWatir and found that I needed to remove session cookies to ensure a clean starting point for my script. XPCOM allowed me to read back cookies and it seemed very likely that it would also facilitate deletion.

The Mozilla Developer Center cookies examples were very close to what I needed. nsICookieManager.remove seemed perfect. In a FireWatir script it looked like what I needed to read/write cookies was something like this:

def removeCookie(brwsr, host, name, path)
   cmd = "Components.classes['@mozilla.org/cookiemanager;1']"
   cmd += ".getService(Components.interfaces.nsICookieManager)"
   cmd += ".remove(\"" + host + "\", \"" + name + "\", \"" + path + "\", false)";
   puts "Removing cookie " + name + " from " + host + " for path " + path
   result = brwsr.execute_script "#{cmd}\n"   
end

def readCookies(brwsr, site)
   cmd = "var uri = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService)"
   cmd += ".newURI(\"" + site + "\", null, null);"
   cmd += "Components.classes['@mozilla.org/cookieService;1']"
   cmd += ".getService(Components.interfaces.nsICookieService)"
   cmd += ".getCookieString(uri, null);"
   result = brwsr.execute_script "#{cmd}\n"   
end

In my initial attempts to use this I passed in "http://myhost" and although the method succeeded subsequent logging of the updated cookie string showed no change at all. After some fumbling around I discovered a detailed example of manipulating cookies through javascript in an extension. The main difference in their example was no protocol:// in front of the host/domain name. After that it worked perfectly. A simple example follows:

require "watir"

protocol = "http";
host = "mytesthost";
initial_path = "/somethingfuntotest"

site = protocol + "://" + host

def url(brwsr)
   return brwsr.js_eval("document.location.toString();")
end

def readCookies(brwsr, site)
   cmd = "var uri = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService)"
   cmd += ".newURI(\"" + site + "\", null, null);"
   cmd += "Components.classes['@mozilla.org/cookieService;1']"
   cmd += ".getService(Components.interfaces.nsICookieService)"
   cmd += ".getCookieString(uri, null);"
   result = brwsr.execute_script "#{cmd}\n"   
end

#Ref https://developer.mozilla.org/en/nsICookieManager
#Ref http://www.springenwerk.com/2007/07/using-cookies-with-firefox-extensions.html
#Usage removeCookie(brwsr, "myhostname", "SessionIdCookieName", "/")
#Note that passing http://myhostname for host does not appear to work
def removeCookie(brwsr, host, name, path)
   cmd = "Components.classes['@mozilla.org/cookiemanager;1']"
   cmd += ".getService(Components.interfaces.nsICookieManager)"
   cmd += ".remove(\"" + host + "\", \"" + name + "\", \"" + path + "\", false)";
   puts "Removing cookie " + name + " from " + host + " for path " + path
   result = brwsr.execute_script "#{cmd}\n"   
end

Watir::Browser.default = "firefox"
brwsr = Watir::Browser.new

#May have a session id
puts "Cookies for " + site + ": " + readCookies(brwsr, site)
removeCookie(brwsr, host, "JSESSIONID", "/")
#Session id is now gone ... yay!
puts "Cookies for " + site + ": " + readCookies(brwsr, site)

Tuesday, April 6, 2010

Watir 101: Javascripting XPCOM to read cookies in a FireWatir script

Today I ran into the need to automate a web UI built primarily of html and javascript. HtmlUnit based solutions seemed nice but Watir seemed like an ideal solution as it would necessarily produce the exact same results a real user would experience.

First step was to get setup, following the instructions at http://watir.com/installation/#win. Despite my complete lack of experience with both Ruby and Watir things went pretty smoothly. My setup sequence was:

  1. Install Ruby (http://rubyforge.org/frs/download.php/29263/ruby186-26.exe)
  2. At new cmdprompt:
    1. We need a new command prompt because one open from before the Ruby install will not notice the updated environment variables and thus will not have ruby on PATH
    2. Note that the second command is slow as hell (at least it was for me)
      gem update --system
      gem install watir
      
  3. Install FF JSSH plugin; ref http://watir.com/installation/#win

  4. Poke around a couple of helpful documents to try to learn the basics

  5. Run scripts similar to ruby my_script.rb

Scripting the basic interaction went pretty smoothly. Obtaining a Firefox browser and jumping around is every bit as straight forward as you might hope. Obtaining the current url is slightly more problemmatic than one might help but luckily others have solved this problem already. Here is an example of a simple script:
require "watir"

site = "http://my.test.host"
initial_path = "/blah"

def url(brwsr)
   return brwsr.js_eval("document.location.toString();")
end

Watir::Browser.default = "firefox"

puts "Requesting a new " + Watir::Browser.default
brwsr = Watir::Browser.new

puts "Loading " + site + initial_path
brwsr.goto site + initial_path
puts "At " + url(brwsr)

puts "Selecting and navigating forward"
brwsr.radio(:name, "inputRadio").set
brwsr.button(:name, "Continue").click
puts "At " + url(brwsr)

The only real problem with basic scripting functionality I ran into (as yet unresolved) is that if something takes longer than Watir likes to wait (say the browser is slow to pop or a page is slow to respond) the script gets ahead of the browser and gets severely confused.

So now for the first real problem I ran into: Cookies. The application I want to test is session-aware so ultimately I will want to nuke cookies at the beginning of the test. First step, lets see if we can get cookie information back. After a bit of searching it turns out manipulating cookies from Watir is less simple than one might hope. For IE people seem to simply use Ruby to delete the files out of the filesystem (see FAQ). For Firefox we can alter cookies via XPCOM, Mozilla even has an example.

This is all well and good but how can we execute javascript against XPCOM objects from Ruby code running FireWatir?? Luckily I found a great series of articles that were almost what I wanted:

The only problem was that the example called for rebuilding some of the code ("I opened up the Firewatir::Firefox class and added...") for FireWatir. This would definitely be stretching my fledgling Ruby/Watir skills so I went looking for a simpler approach.

The browser object for Firefox (FireWatir::Firefox) exposes the wonderful execute_script method. The description "Executes the given JavaScript string" sounds pretty promising. A quick check of the source firefox.rb file showed that the method is a thin wrapper around the same JSSH interaction sequence that is used in the http://sticklebackplastic.com articles. This all seemed almost too good to be true so I decided to see if it actually worked the way it appeared. Luckily it works just as you would hope; the following example shows Ruby code reading cookies for whatever domain it happens to be interested in:

require "watir"

site = "http://my.test.host"
initial_path = "/blah"

def prepare(brwsr, site)
   puts "Preparing..."
   cmd = "var uri = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService)"
   cmd += ".newURI(\"" + site + "\", null, null);"
   cmd += "Components.classes['@mozilla.org/cookieService;1']"
   cmd += ".getService(Components.interfaces.nsICookieService)"
   cmd += ".getCookieString(uri, null);"
   result = brwsr.execute_script "#{cmd}\n"
   
   puts "Cookies for " + site + ": " + result
end

Watir::Browser.default = "firefox"

puts "Requesting a new " + Watir::Browser.default
brwsr = Watir::Browser.new

showcookie(brwsr, site)
showcookie(brwsr, "http://www.bing.com")
Hopefully a similar mechanism will allow me to alter cookies but that's as far as I've made it so far. Seems quite promising! And there are lots of cool names (Watir, Ruby, JSSH, XPCOM, ...) involved, always a plus!

I was very impressed that starting with no knowledge of Ruby or Watir I was able to get everything installed and create a functional proof of concept script (albeit one very low on validation or error checks) for a semi-complex sequence (add item to cart and checkout) in a couple of hours! If I can get cookie removal working to ensure a clean start state this could grow to be a very valuable verification tool for our developers.

Wednesday, March 24, 2010

AWK'ward logs: Finding errors by type or time with bash

Just in case I ever need to do this again, a couple of notes about quickly summarizing error logs using Bash. Script examples all run on Fedora 6.

Error By Time
Recently I ran into a couple of log files (multiple nodes and log rolling) with more errors than I cared to examine one by one.

2010-03-10 06:32:57,982 ERROR CasLoginView:0 - CAS Login failedin btnLogin_Click. Root error = Could not send Message.
  System.Web.Services.Protocols.SoapHeaderException: Could not send Message.
  at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
  at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
  at ...etc...
2010-03-10 07:47:21,900 ERROR HBXTaggingControl:0 - Error initializing
  System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
  at System.ThrowHelper.ThrowKeyNotFoundException()
  at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
  at ActiveFoundation.ConsumerUI.PageTypeCore.HBXTaggingControl.PrepareHBXScript()
  at ActiveFoundation.ConsumerUI.PageTypeCore.HBXTaggingControl.OnInit(EventArgs e)
2010-03-10 07:50:23,759 ERROR HBXTaggingControl:0 - Error initializing
  System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
  at System.ThrowHelper.ThrowKeyNotFoundException()
  at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
  at ...etc...

In order to start making sense of it all I rapidly found I needed summaries: errors by time, by type, and so on.

The first and most basic need was to filter down to just the errors per hour for a specific day. The desired result is date, hour, number of errors with entries for each hour, not just the ones that have errors.

2003-03-10 00 0
2003-03-10 01 12

If we just wanted an entry for every hour with more than 0 errors we could just grep and count. To get entries for hours that have no entries in our log it is easiest to just loop through the days/hours desired. Looping through days and hours desired is easy enough in a Bash script.

The dates in the log are padded with leading zeros so to quickly get a similar effect in Bash we can use printf to produce the statement we want. Putting this together, we start by writing a simple loop script that builds up roughly the statement we want and stores it into a variable:

#!/bin/bash

wd=`pwd`
for day in {23..23}
do
   for hr in {0..23}
   do
      cmd=`printf "grep \"^2010-03-%02d %02d\" ${wd}/*.log  | grep ERROR | wc -l" ${day} ${hr}`
      echo ${cmd}
   done
done

Note that the cmd= line uses backticks (old school syntax for command substitution) to execute printf and capture the result in a variable. It would be more modern of us to use $(printf...) instead. If we run this script it will print the series of commands we wish to execute to find the error count for each hour:


[build@localhost dm-logs]$ ./blog.sh 
grep "^2010-03-23 00" /home/build/dm-logs/ConsumerUI1.log /home/build/dm-logs/ConsumerUI2.log /home/build/dm-logs/ConsumerUI3.log | grep ERROR | wc -l
grep "^2010-03-23 01" /home/build/dm-logs/ConsumerUI1.log /home/build/dm-logs/ConsumerUI2.log /home/build/dm-logs/ConsumerUI3.log | grep ERROR | wc -l
...etc...


So far so good, but we'd really much prefer to actually execute the command! It turns out we'd really like to print the day and hour, then the grep command output (eg the error count for that hour) so we'll modify the command a little bit while we're at it:

#!/bin/bash

wd=`pwd`
for day in {23..23}
do
   for hr in {0..23}
   do
      eval $(printf "echo \"2010-03-%02d %02d \" \$(grep \"^2010-03-%02d %02d\" ${wd}/*.log  | grep ERROR | wc -l)" ${day} ${hr} ${day} ${hr})
   done
done

What's going on here is that we're building up a command using printf, capturing the result by way of command substitution $(printf ...blah...), then executing the result using eval. The statement we run eval on will look like this:

echo "2010-03-23 23 " $(grep "^2010-03-23 23" /home/build/dm-logs/ConsumerUI1.log /home/build/dm-logs/ConsumerUI2.log /home/build/dm-logs/ConsumerUI3.log | grep ERROR | wc -l)

We're going to echo the date echo "2010-03-23 23 " followed by the output (courtesy of command substitution) of the command that counts up error entries for the date $(grep "^2010-03-23 23" /home/build/dm-logs/ConsumerUI1.log /home/build/dm-logs/ConsumerUI2.log /home/build/dm-logs/ConsumerUI3.log | grep ERROR | wc -l).

The result looks might look something like this:

2010-03-23 00  0
2010-03-23 01  0
2010-03-23 02  0
2010-03-23 03  0
2010-03-23 04  0
2010-03-23 05  0
2010-03-23 06  54
2010-03-23 07  304
2010-03-23 08  236
2010-03-23 09  320
2010-03-23 10  175
2010-03-23 11  199
2010-03-23 12  109
2010-03-23 13  0
2010-03-23 14  0
2010-03-23 15  0
2010-03-23 16  0
2010-03-23 17  0
2010-03-23 18  0
2010-03-23 19  0
2010-03-23 20  0
2010-03-23 21  0
2010-03-23 22  0
2010-03-23 23  0

From such a result we might infer that we had an exciting morning! This type of output works very nicely in Excel as a series so we can get a pretty picture out of it, graph and compare multiple series, and so on.

Error By Type
In addition to wanting to know what is failing by time, we may wish to know where the failures are coming from. Our log format gives this to us, so lets dig up the source components and count up how many errors each one is filing. The source is the bit highlighted in bold below:

2010-03-10 06:32:57,982 ERROR CasLoginView:0 - CAS Login failedin btnLogin_Click. Root error = Could not send Message.

So, first step is how do we pull out the source for each error. Our tokens are roughly space-separated so they'll fit perfectly into awk (book, brief introduction). For our purposes it is sufficient to yank the fourth token out, then throw away the duplicates:

grep ^2010-03-23 *.log | grep ERROR | awk '{ print $4 }' | sort | uniq -c

This will give us a nice list of error count by source, similar to:

.
      7 CasLoginView:0
     34 SomeOtherComponent:0

This is nice, but maybe what we'd like is to see the count of each exception message from each source. We can just loop through the sources, grab the errors from each source, drop the time part (by substituting "" for those fields using awk), then count it all up:

#!/bin/bash

wd=`pwd`
for source in `grep ^2010-03-23 *.log | grep ERROR | awk '{ print $4 }' | sort | uniq`
do
  grep "^2010-03-23 " *.log  | grep "ERROR ${source}" | awk '{v=$0; gsub($1" "$2" "$3" "$4" - ", "", v); print $4" "v; }' | sort | uniq -c
done

This will give us something like:

.
      3 LoginControl:0 Some clever failure message
     54 LoginControl:0 Another clever failure message
      1 LoginControl:0 A rare but still fascinating failure

Like most things in Bash there are nearly endless ways to make such little scripts faster, shorter, and so on.

Friday, March 5, 2010

The Joy of Blogger Syntax Highlighting

On a whim I decided to setup a blog where I could capture the odd technical thought, primarily to replace my trusty notebook (the paper kind) which turns out to be a bit prone to getting lost, coffee coated, and so on.

On encountering some problems with Spring and thread safety I decided to scratch a few notes about it. The post included a little bit of Java and XML so I thought it would be nice to have it highlighted neatly. Ninety minutes later I had tried several variants of setting up SyntaxHighlighter on blogger without using any of my own hosting. Most of these methods did not work or only partially worked. Ironically most of the methods I tried were posted on other peoples blogs. The closest one to working out of the box was this one. The result was the text formatted but none of the styles applied properly.

On inspection of a blog where the highlighter seemed to be working (http://alisteroz.blogspot.com/) it appeared that the styles were simply copied inline. So, the steps that ultimately worked for me are:

  1. Open the blogger Layout tab and click 'Edit HTML'
  2. Locate <head/> and add the core syntax highlighting script, plus the script(s) specific to any types of code you wish to post. In my case:
    <script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shCore.js'></script>
    <script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCSharp.js'></script>
    <script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushXml.js'></script>
    <script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushSql.js'></script>
    <script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushJava.js'></script>
    <script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCss.js'></script>
    <script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushJScript.js'></script>
    
  3. Open http://syntaxhighlighter.googlecode.com/svn/trunk/Styles/SyntaxHighlighter.css and copy/paste the entire content into the layout just before the --></style>. This will typically be just above the spot where the .js file <script> tags were added
  4. Locate the </body> tag and add the following just before it
    <script language="javascript">
    dp.SyntaxHighlighter.BloggerMode();
    dp.SyntaxHighlighter.HighlightAll('code');
    </script>
    
  5. Place code, html escaped (many online tools, such as http://accessify.com/tools-and-wizards/developer-tools/quick-escape/default.php will do this for you) into pre blocks
    <pre class="Xml" name="code">
    &lt;script language='javascript'
    </pre>
    

All in all much less easy than I had expected!! Note that you must have the brush (shBrushlanguage.js) script imported from http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/ to get highlighting for the corresponding language.

Thread safety (or not) of Spring singletons using setter injection

Recently I have seen several instances, one of which would have caused very serious defects had it been released, of very experienced programmers creating code with thread safety issues due to lack of familiarity with the implications of the Spring singleton scope.

It is very common we create a class:

public class MySingleton {
    private int myVar;

    public void setMyVar(int myVar) {
      this.myVar = myVar;
    }

    public int getMyVar() {
      return myVar;
    }
  }

And configure it in Spring:

<bean class="com.example.spring.threading.MySingleton">
    <property name="myVar" value="7" />
  </bean>

So far so good... or is it? We've got a mutable instance variable on a class that is setup as a singleton. As soon as we configure it to be used by something that runs on multiple threads we've got a serious potential problem. Since the majority of our Spring applications are web applications they will very naturally tend to utilize some of these beans from request processing threads so we'll end up with concurrent access. Normally it is a non-issue: the property is only ever set through Spring so even though it is sitting there mutable we don't actually get any problems. However, this is not guaranteed by our code; it's essentially accidental that it works.

So, moving right along, our hapless developer - who is, btw, a Java expert though perhaps not a Spring expert yet - creates some instance variable and a method that can alter its value.

public class MySingleton {
    private int myVar;
    private List myList;

    public void setMyVar(int myVar) {
      this.myVar = myVar;
    }

    public int getMyVar() {
      return myVar;
    }
    
    public void doSomethingToMyList() {
      //...code that alters myList...
    }
  }

Looks fine, the type doesn't show any signs of needing to be threadsafe, right? This looks fine in unit testing, works fine when they try the software through a web browser, and then blows up (sometimes) when the software is used by concurrent users.

Since our singleton is probably really intended to be immutable (or would be if the developer stopped and thought it through) we could presumably set it up with final fields and an initialization constructor whose arguments are provided via Spring constructor injection. This is rather ugly in Spring due to the inability to provide named constructor-arg values (which is in turn due to lack of parameter name metadata in Java). Our developers strongly favor setter injection and the Spring docs on the subject seem to generally recommend this approach so for now we're left with nothing but code reviews between us and threading disaster.

This sucks. None of our current automated checks will notice. A code review should catch it but inevitably some will slip through. Possibly we can use annotations with @Resource on the field, as suggested in http://www.infoq.com/articles/spring-2.5-part-1, and provide only a getter. This is probably the most promising solution at present.