Browser Detection: How Not To Do It

Web browsers used to behave so differently from one another that it was common to write code to detect which browser type and version was being used to display the page so that appropriate code could be run. Fortunately, modern browsers tend to support web standards better and so apart from a few CSS tweaks it is unusual to come across browser detection code (the one exception to this rule being the expense system I have to use at work which doesn't seem to like Firefox at all). So I was a little surprised the other day when I suddenly found myself bounced to a an unsupported browser page on a shopping site that I'd been browsing around a few days before. So being the inquisitive kind I had a dig around and came across this gem.
if (navigator.userAgent.match(/Firefox\/[12]/)) {
   window.location.href = "unsupported.html";
}
For those of you who don't speak JavaScript, this essentially checks (using a regular expression) for the presence of either "Firefox/1" or "Firefox/2" in the useragent string, which identifies the make and version of the web browser requesting the page.

Now I like living on the bleeding edge of browser development and so I run the nightly builds of Firefox. Given the recent change in the release cycle of Firefox, the version number has climbed quite rapidly and the nightly build now sends the following useragent string (you can find out where your browser sends using this website):

Mozilla/5.0 (X11; Linux x86_64; rv:10.0a1) Gecko/20111012 Firefox/10.0a1

So from reading this you should be able to see that I'm actually running version 10.0a1 of Firefox. It should also be clear why the check of my browser resulted in me being bounced to the unsupported browser page: Firefox/10.0a1 contains the string "Firefox/1". This is a good example of why you really shouldn't write your own browser detection code, especially as there are a number of well written and up to date scripts out there that correctly extract the make and version number and which are easy to use. And if you subscribe to the not-invented-here school of thought, then at least make sure you actually implement a sensible solution!

Postvorta: Providing Intelligent Blog Search

The eagle-eyed amongst you may have noticed that about a month ago the search box in the sidebar of this blog changed. I used to use the standard Google search gadget but I now use a gadget powered by Postvorta.

Postvorta was built specifically to enable intelligent searching of blogs. How do I know this you ask? Well I spent the past year building Postvorta in my spare time. The initial motivation was a number of conversations with fellow bloggers about the inadequacies of the Google search gadget and coupled with the fact that my job involves processing natural language documents (I work as part of the GATE group at the University of Sheffield) I thought I was in a position to provide something better.

It is difficult to know exactly how the standard Google search gadget works, but as far as I can tell (both from personal experimentation and from talking to others) it appears to only index the main content of each post. For example, it certainly doesn't index the labels associated with posts. This means that while you can view all posts with a given label you can't search for them using the search gadget. Postvorta, however, indexes all the important content from your blog posts: title, article, labels, and comments. Importantly it does not index the pages you see when you view the blog in a web browser, instead it access the underlying data (via the Google Data APIs) which means that it can ignore the repeated information in the blog template. For example, many blogs contain a gadget which lists recent post titles, these shouldn't be indexed with each post as that makes it much more difficult to search for the actual post. A search can also be restricted by date and/or by the people who commented on a post. I've tried to provide as much flexibility as possible while keeping the full interface relatively simple.

Fortunately when building Postvora I didn't have to start from scratch. One advantage of working in a research group that makes their software available under an open-source license is that I can make use of software I use at work in my own projects. In this case the main indexing and search facilities behind Postvorta are built upon GATE Mímir. I've talked about Mímir before on this blog and if you've read that post then you shouldn't be surprised that as well as searching for words, like Google and the other search engines, you can use Postvorta to search your blogs semantically, i.e. for things. So you can search for any posts containing, for example, the name of a person without knowing what the name was in advance. If you are new to Mímir then Postvorta provides a comprehensive description of the query syntax which becomes available when you choose to use it through the search interface (by default searches are treated as a simple bag-of-words just as with other search engines).

Feel free to have a play with Postvorta through the search gadget on this blog. I'm also using it on my main blog where there are a lot more posts to search through. Postvorta is currently being run as a closed beta (while I evaluate performance, reliability etc.) but if you like what you see then you can register your interest and I'll try and index your blog as soon as possible -- note that currently Postvorta only supports Blogger blogs, although WordPress support should be coming soon.

Let me know what you think.

Blogger's Lightbox Returns To Haunt Our Blogs!

Blogger have now reintroduced lightbox to all blogs. While they claim to have fixed a lot of the bugs/issues that were reported before, they have still turned lightbox on by default. Fortunately we don't need to apply a hacky workaround this time.

If you don't want to use lightbox then you can turn it off for each blog you write through your dashboard. Simply select “No” next to Lightbox in the Settings > Posts and Comments section (new interface) or the Settings > Formatting section (old interface). I still think this feature should be opt-in rather than opt-out but at least they are allowing us to opt-out, so I guess we should be grateful.

Don't Look Down

And now for something completely different -- a posting not at all related to Blogger!

I've recently been spending quite a bit of my free time playing around with GATE Mímir (the reasons for which will become clear in a later post). As I've mentioned before, Mímir is a multi-paradigm indexing and retrieval system which allows us to combine text, annotations and knowledge base data in a single index. Text within Mímir is indexed using MG4J and by default is processed (at both indexing and search time) by a DowncaseTermProcessor which ensures that searches are case insensitive. Unfortunately while case insensitive searching is great there are other common problems when searching text collections, one of which can be nicely illustrated just from the name Mímir.

Whilst the name of the system, Mímir, contains an accented character, most people when searching would probably not go to the bother of figuring out how to enter the accented i and would instead try searching for Mimir. But just as Mímir and Mimir are visually different, so are they different when stored in an MG4J index. In other words if we search using the unaccented version we won't get any results! Whilst Mímir is a slightly unusual case I'm sure that we can all agree that a search for cafe should also bring back documents which mention café.

Now for latin alphabets I could come up with a mapping that would reduce most accented characters down to an unaccented version, but it would be time consuming to build and wouldn't handle the different ways in which accented characters can be encoded using Unicode. So I had a bit of a hunt around and discovered a simple, and I think, elegant way of converting accented characters to their unaccented forms courtesy of a blog posting by Guillaume Laforge. Creating a custom MG4J term processor using this code was trivial and so I now have a way of ensuring that accented characters don't cause me any problems. The one issue was getting Mímir to use the new term processor.

I deploy Mímir in a Tomcat instance by building a WAR file and while I could simply add a JAR file containing my custom term processor to the WEB-INF/lib folder before creating the WAR I'd prefer not to have to. If i included my code within the Mímir WAR then anytime I wanted to make a change would require rebuilding the WAR and redeploying which seems to be more work than necessary. Fortunately the Mímir config file allows you to specify GATE plugins that should be loaded when the web app is started. So it is trivial to create a GATE plugin which references a JAR containing my custom term processor. Unfortunately when I tried this, MG4J threw a ClassNotFoundException. The problem is that Java never looks down.

On the right you can see the ClassLoader hierarchy that is created when Mímir is deployed in Tomcat -- I've added little icons to show which are created by the Java runtime environment, which by Tomcat and which by the GATE embedded within Mímir. As you can see the GATE classloader, which is responsible for loading the plugin containing my custom term processor, is right at the bottom of the hierarchy. The MG4J libraries in the Mímir WEB-INF/lib folder which is the responsibility of the Web App classloader. Each classloader only knows about it's parent and not about any children and when asked to load a class first asks it's parent classloader and only if the class cannot be loaded does it then try loading it itself. The problem I was facing was that when MG4J tried to load my custom term processor it did so by asking the Web App classloader and as it is loaded by a child classloader the class couldn't be found and hence a ClassNotFoundException was thrown. Rather than giving up and simply adding the term processor to the WEB-INF/lib folder I decided to see if I could find a way of injecting the term processor into the right classloader.

Now before we go any further I should point out that one of my collegues has described what follows as evil, and I have to say I agree with him. That said it works and given the way Mímir works I can't see any problems arising, but I wouldn't suggest this as a general solution to the class loading problem described above for reasons I'll detail later. However....

Each class in Java knows which ClassLoader instance was responsible for creating it and we can use this information to forcibly inject code into the right place, using the following method.

private static void codeInjector(Class<?> c1, Class<?> c2)
{
  try
  {
    // Get the class loader which loaded MG4J
    ClassLoader loader = c2.getClassLoader();

    if (!loader.equals(c1.getClassLoader()))
    {
      //Assuming we aren't running inside the MG4J class loader...

      //Get an input stream we can use to read the byte definition of this class				
      InputStream inp = c1.getClassLoader().getResourceAsStream(c1.getName().replace('.', '/') + ".class");

      if (inp != null)
      {
        //If we could get an input stream then...

        //read the class definition into a byte array
        byte[] buf = new byte[1024 * 100]; //assume that the class is no larger than 100KB, this one is only 3.5KB
        int n = inp.read(buf);
        inp.close();

        //get the defineClass method
        Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);

        //defineClass is protected so we have to make it public before we can call it
        method.setAccessible(true);

        try
        {
          //call defineClass to inject ourselves into the MG4J class loader
          method.invoke(loader, null, buf, 0, n);
        }
        finally
        {
          //set the defineClass method back to being protected
          method.setAccessible(false);
        }
      }
    }
  }
  catch (Exception e)
  {
    //hmm, something has gone badly wrong so throw the exception
    throw new UndeclaredThrowableException(e, "Unable to inject " + c1.getName() + " into the same class loader as " + c2.getName());
  }
}

In essence this method injects the definition of class c1 into the classloader responsible for class c2. So in my term processor I call it from a static initializer as follows:

static
{
  codeInjector(NormalizingTermProcessor.class,
    it.unimi.dsi.mg4j.index.Index.class);
}

So how does this all work. Well hopefully the code comments will help but...
  1. Firstly we check that the classes are defined by different classloaders (lines 6-8)
  2. Then we convert the class name into the path to the class file and try and open a stream to read from that file (line 13). If we can't read the class file then it means we have already injected the class which is why the classloader can't find the class file.
  3. We then read the class file into a byte array (lines 20-22)
  4. To inject code into a classloader we need to use the defineClass method, which unfortunately is protected. So we retrieve a handle to the method and remove the protected restriction (lines 25-29)
  5. We now call deifneClass on the classloader we want to know about the class passing in the bytes we read in from the original class file (line 33)
  6. Finally we put the protected restriction back so we leave things as they were when we found them (line 38)
Now there are a couple of things to be aware of which could trip you up if you try and do something similar:
  1. If there is a security manager in place then you may find that you can't call the defineClass method even when the protected restriction is removed.
  2. This code will result in the same class being defined in two classloaders (which after all was the whole point) but instances of the class cannot be shared between the classloaders. If you try to you will get an exception (can't actually remember which one).
Neither of these seem to be an issue with loading custom MG4J term processors into Mímir, so this seems to be a nice, albeit evil, way of allowing me to add functionality without having to add to the Mímir WAR file. Success!

Followers, What Followers?

Now I don't want people to think that this blog is just about code for fixing problems with Blogger but... for this post I'll be fixing a problem with Blogger!

I don't tend to read my own blog that often, which means I don't spend much time looking at it. So it wasn't until I started investigating how to disable Blogger's forced lightbox viewer that I spotted I had a problem with the followers gadget on some of my blogs. Developing my original fix to that problem involved reloading my blog over and over again as I tried different things. Sometimes the followers gadget appeared and sometimes it didn't. There didn't seem to be any pattern but there was definitely a problem.

Given that a lot of the gadgets you find on blogs are JavaScript based the first thing I checked was the JavaScript console (I use Firebug in Firefox for most of my web development work) which showed the following two errors whenever the followers gadget failed to appear; window.googleapisv0 is undefined and google.friendconnect.container is undefined.

Given that the problem was intermittent I made a guess that this was some form of race condition. Most browsers will try and load at least two files required to render a web page in parallel. This means that sometimes files download and become available to the browser in a different order. Usually this doesn't matter but my gut feeling was that this was causing the problem. A quick look at the list of JavaScript files associated with my blog showed that there were quite a few related to the followers widget and that were all loaded at roughly the same time. I experimented by manually adding each of these scripts to the head section of the HTML template until I eventually found a solution.

Strangely it wasn't a script associated directly with the followers gadget that solved the problem, but rather a script for the +1 button. This did, however, explain why I was only seeing the problem on my blogs which included the sharing buttons below each post. So if you find that your followers gadget sometimes doesn't appear then it might be worth trying the following fix.

You need to edit the HTML version of your template (you might be able to do this via a HTML/JavaScript gadget as well but I've had less success) so in the old style Blogger dashboard go to the design page or in the new style go to the template page and then click to edit the HTML version of your template. Now directly after <head> insert the following:

<script src='https://apis.google.com/js/plusone.js' type='text/javascript'/>

This fixed the problem for me, and I've suggested the fix in a couple of threads on the Blogger forum and it seems to have worked there as well, so hopefully it should stop your followers gadget disappearing for good!

Fixing Blogger's Mistakes

UPDATE: Blogger have finally stopped forcing the lightbox viewer upon us, which means the fix detailed in this post is no longer required! I'll be watching though and if it reappears as the default option, with no ability to turn it off, then I'll update the fix so that we can continue to choose how our images are displayed.

Yesterday Blogger introduced a new 'feature' to some blogs. Now images appear in a Lightbox powered overlay. Unfortunately a lot of people think that this feature is actually a bug. On one of my other blogs, it is a really problem due to the fact that I was already using a different script to achieve a similar affect. With the new feature I now get two popup copies of each image which really is horrid. So I spent a good hour trying to find a hack or workaround, until Blogger sees fit to allow us to disable the bug.

The main part of the fix is a simple piece of Javascript.

<script type='text/javascript'>
//<![CDATA[
 var images = document.getElementsByTagName('img');
  for (var i = 0 ; i < images.length ; ++i) {
    images[i].parentNode.innerHTML = images[i].parentNode.innerHTML;
  }
//]]>
</script>

The fix works because the new Blogger code adds an onClick function to the actual image, whereas most people wrap the images in a link. What I wanted to do was simply remove the onClick function but I couldn't figure out how (and believe me I tried), but simply recreating the image removes any registered events. The problem is ensuring that this code runs after the code Blogger used to add the lightbox viewer.

The trick to getting this code in the right place (thanks to Bonjour Tristesse for this bit) involves editing the HTML version of your template. From the Design page in the old Blogger dashboard or from the Template page in the new version bring up the HTML version of your template and then place the code almost at the very end, right between </body> and </html>

If you aren't happy editing the HTML version of your template then you can also add the fix via a gadget. Simply go to the layout editor and add a new HTML/Javascript gadget (it doesn't matter where). Leave the title of the gadget blank and paste in the following code.

<script type="text/javascript">
//<![CDATA[
var lightboxIsDead = false;
function killLightbox() {
  if (lightboxIsDead) return;
  lightboxIsDead = true;
  var images = document.getElementsByTagName('img');
  for (var i = 0 ; i < images.length ; ++i) {
     images[i].parentNode.innerHTML = images[i].parentNode.innerHTML;
  }
}
 
if (document.addEventListener) {
  document.addEventListener('DOMContentLoaded', killLightbox, false);
} else {
  document.attachEvent('onDOMContentLoaded', killLightbox);
  window.attachEvent('onload', killLightbox);
}
//]]>
</script>

Save the gadget and you are done. The fix will have been applied and things should be back to how they were before Blogger introduced this bug/feature. If/when Blogger see sense and allow us to disable this feature then you can easily delete my workaround simply be deleting the gadget from your layout. Note that applying the fix by editing the HTML version of your template is slightly more reliable, but in most cases you won't see any difference between the two.

Now I'm quite happy to let each individual blog owner choose how to display their photos, and some might even like the new photo viewer. From reading the forums, however, it is clear that some people just really hate the new viewer and would prefer not to see it even on other people's blogs. Well it turns out that the above fix also works when used as a Greasemonkey script. If you already have Greasemonkey installed in your browser then you can simply install the script to kill Blogger's lightbox on all Blogspot hosted blogs. If you don't have Greaemonkey installed then the Wikipedia page should point you to a version for your favorite browser.

UPDATED 17th September: I've simplified the script slightly and added a fix so that if the mouse was already within an image when the page loaded the fix will still apply if you click the image, assuming you move the mouse at least one pixel in any direction.

UPDATED 17th September: I've edited the post to suggest that the fix is used via a HTML/Javascript gadget so that new readers don't have to wade through the comments to find this out.

UPDATED 17th September: Now we specify false in the addEventListener call to ensure better backwards compatibility with older versions of Firefox.

UPDATED 20th September: Added Bonjour Tristesse's much better fix as the main suggested workaround.

UPDATED 21st September: Added the section on using the newest fix as a Greasemonkey script to kill Lightbox on all Blogspot hosted blogs.

UPDATED 21st September: Simplified the new fix slightly to do the replace inside body instead of the main div. This means that it will work even if you have heavily modified a template to no longer have the named div assumed by the previous version.

UPDATED 21st September: The old method now registers the function so it is fired when the DOM is loaded not the page. This should mean it works even before the page has fully loaded.

UPDATED 21st September: Simplified the short fix, as the replacement isn't actually required to make it work. This cuts down on the number of bytes served and should run quicker as well!

UPDATED 21st September: Switched back to recommending the gadget based fix (albeit a simpler version) because Bonjour Tristesse's version actually breaks other widgets within the posts, such as the Google +1 button in the post sharing widget. Fortunately the new and improved gadget version is applied much quicker and so seeing the viewer is much less likely than before.

UPDATED 22nd September: Only replace the actual image, not the entire content of the parent element. This should reduce the number of situations in which there is a chance of breaking any other scripts or gadgets.

UPDATED 22nd September: Attach to both onDOMContentLoaded and onLoad when running under IE to ensure the code gets run regardless of which version of IE we are using, but make sure we don't try and run twice as that is pointless.

UPDATED 22nd September: Rewrote the post to show that the same fix can be applied both by editing the HTML template or by adding a gadget. The difference from before is that now the HTML template based fix won't break the sharing buttons etc.

UPDATED 22nd September: No longer use cloneNode as IE actually clones the event handlers so the viewer still appears.

Trawling The Heap

I've spent a good few hours over the last week trying to track down a memory leak in a web application I've been working on. As far as I could tell from the code all the relevant resources were being freed when finished with, but still after a few hours the tomcat instance in which the app was running would grind to a halt as the available free memory inched ever closer to zero. In the end I decided that that only solution was to trawl through a heap dump to find out exactly what was being leaked and what was holding a reference to it.

Now it used to be that taking exploring the Java heap was a tedious and horrid process. Fortunately, the JDK now comes with VisualVM that makes working with the heap really easy.

VisualVM can attach to any running Java process and monitor it's memory usage, which in itself can be useful, but it can also take a heap dump and then provides an easy tool for navigating through the often vasts amount of information provided. Now in theory you should be able to use VisualVM to examine the heap of the tomcat server running a troublesome web app. Now try as I might I couldn't get this to work. The problem stems from the fact that I'm running tomcat under a different user account than my own, an account that you can't actually log in to (for the curious I installed tomcat under Ubuntu using the default package which runs tomcat under the tomcat6 user). I could monitor the memory usage but no matter what I tried (and believe me I tried all sorts of things) I couldn't manage to get a heap dump.

In the end I resorted to manually creating a core dump using the unix gcore utility and then loading this into VisualVM which could then generate a heap dump. This actually works quite nicely. The only downside is that it requires you to know the process ID of the tomcat web server and this changes everytime the server is restarted, which if you are debugging a problem, can be quite often. So to make my life a little easier I've written a small bash script that makes tomcat dump it's heap, which I've cleverly called tomscat!

#!/bin/bash

pid=`ps -u tomcat6 | grep java | sed 's/ .*$//g'`
count=0

ls -1 tomcat*.$pid > /dev/null 2>&1

[ $? -eq 0 ] && count=`ls -1 tomcat*.$pid | wc -l`

gcore -o tomcat$count $pid

This script firstly finds the pid for the tomcat process then works out if there are already any core dumps for this instance of tomcat and then generates a core dump into a nicely named file. Currently there is little in the way of error handling so if it doesn't work any errors may be cryptic! Anyway hopefully other people might find this script useful, I know it made the process of creating a bunch of heap dumps quite easy, and once I had the heap dumps tracking down the leak was fairly easy (turns out the the leak was due to large cache objects associated with database connections not being made available for garbage collection).