Friday, November 12, 2010

Two nice wines and Baguette

I don't have a very good way to record memorable wines. So let me note a couple here from the last couple of months before I forget them: 2008 Langmeil Orphan Bank and 2004 Reschke Bos.

And while I'm typing epicuriously, let me also note that two work friends and I had a great meal at Baguette this week. All three of us noted the complementary flavours of our dishes - the duck, the rabbit, and my pork belly, boudin noir, lentils de puy, and garlic snails. Can't wait to go back there again.


Playing with Batik

Helen's group at school has been asked to put together an "interactive trade-show booth" on the topic of Indonesia. I decided that this was a good excuse to try to develop some kind of quiz to help. It seemed that a geographical one would be good.

The concept involved a map, and two types of questions: (a) the user is asked to click on a particular place (island, country, city), and (b) a particular place is highlighted visually on the map, and the user has to choose/guess the name of that place using a multiple choice answer.

I didn't want to build a full scale GIS, so SVG was the obvious answer. I found a great map of the world at wikimedia commons. Using Inkscape, I could crop it and choose an appropriate part of SE Asia - essentially a square covering Burma to Tasmania. The really nice thing about that map is that all the paths are neatly grouped into countries, and they're even labelled inside the SVG source. (The Inkscape XML Editor was invaluable for understanding the structure and locating different paths).

Although groovy is my new language of choice at work, I decided to use plain java/swing for this project, with Apache Batik for the SVG interface. It was easy to highlight a particular country by using the DOM interface to find elements with the appropriate id, and setting the style attribute. I found later that it was critical to do this in the correct thread, and also that you must wait at startup until the map has been rendered.

I found it somewhat frustrating though, to try to detect mouse clicks on the relevant countries. I found that there were two steps necessary: I had to register as a listener for the "click" event on the main layer element - that was obvious enough. But I also had to set an onclick attribute on that element. I don't really know why, but if I don't do it, it doesn't work.

  Element e = getElementById("layer1");
  EventTarget t = (EventTarget) e;
  t.addEventListener("click", this, false);
  e.setAttribute("onclick", "var x = 1;"); // not sure why

I was quite diligent with my test-driven development of the main controller, and used Mockito extensively. It really was fun, and (I'm convinced) much faster than trying to do a big-up-front-design.

The final stage was to package it all up as a single jar. I'd used Simon Tuffs' clever one-jar before at work, and knew that it would be the best thing to use. All of those 15 or so Batik jars, plus the ever-so-useful miglayout - I didn't want to have all those files floating around, having to manually set up a classpath.

There was one extremely frustrating part, though. I kept getting an "Invalid CSS Document" error, with this stacktrace:
Invalid CSS document.
mapquiz.jar (The system cannot find the file specified)
   at org.apache.batik.css.engine.CSSEngine.parseStyleSheet(CSSEngine.java:1149)
   at org.apache.batik.dom.svg.SVGDOMImplementation.createCSSEngine(SVGDOMImplementation.java:117)
   at org.apache.batik.dom.ExtensibleDOMImplementation.createCSSEngine(ExtensibleDOMImplementation.java:212)
   at org.apache.batik.bridge.BridgeContext.initializeDocument(BridgeContext.java:378)
   at org.apache.batik.bridge.GVTBuilder.build(GVTBuilder.java:55)
   at org.apache.batik.swing.svg.GVTTreeBuilder.run(GVTTreeBuilder.java:96)


I didn't even think that I needed or was using any CSS. After googling unproductively, I looked at the source, and discovered line 113 in SVGDOMImplementation.createCSSEngine()
 URL url = getClass().getResource("resources/UserAgentStyleSheet.css");

I tried simply making a resources directory and CSS file inside my one-jar, but that didn't work. After sorting out the differences between Class.getResource() and ClassLoader.getResource(), I decided that it was going to be looking for /org/apache/batik/dom/svg/resources/UserAgentStyleSheet.css. So I created that nest of directories, jar'd up the one-jar, and it worked. There's no way that I'm a classloader expert, but that area of java is now a little less mysterious than it once was.

Just need to see what grade I get now...

Sunday, July 18, 2010

Favourite songs

Ken, a work colleague, said that there had been a recent discussion about peoples' top ten favourite songs. I thought that sounded like an interesting challenge. So at the risk of saving a few fairly fickle favourites forever, here is what I came up with (in roughly chronological order, rather than order of preference).
  1. Pange Lingua (Gregorian)
  2. Beatus Vir (Monteverdi)
  3. Schafen können sicher weiden (Bach)
  4. Nacht und Träume (Schubert)
  5. Im Abendrot (Richard Strauss)
  6. Come away, death (Quilter)
  7. I've got you under my skin (as sung by Diana Krall)
  8. Somewhere (Berstein)
  9. Baby Grand (Joel)
  10. Chili con carne (Real Group)
Perhaps these would be my "desert island discs", rather than absolute favourites. I do like some variety, even if I do listen to those Four Last Songs (Strauss) enough to bore my family.

Wednesday, February 24, 2010

Testing request- and session-scoped spring beans with JUnit

I recently updated my web app to use a UserDetails bean that has request scope. Then I found out that my integration tests didn't work.

java.lang.IllegalStateException: No Scope registered for scope 'request'
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
 at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
 at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.getTarget(Cglib2AopProxy.java:657)
 at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:608)
...

The Spring Documentation made it quite clear:
The request, session, and global session scopes are only available if you use a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext). If you use these scopes with regular Spring IoC containers such as the ClassPathXmlApplicationContext, you get an IllegalStateException complaining about an unknown bean scope.

I looked for solutions, and found Thomas Webner's and Andreas Höhmann's, but they weren't really what I wanted. After a bit of messing around, I found that it was easiest to continue using the existing ApplicationContext (which was actually a GenericApplicationContext, not web-aware), and to add a "request" scope to it. This may not be sufficient once I do more complex tests, but it seems ok for now.

   @Before
   public void setupRequestScope() {
      if (applicationContext instanceof GenericApplicationContext) {
         GenericApplicationContext context = (GenericApplicationContext) applicationContext;
         ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
         Scope requestScope = new SimpleThreadScope();
         beanFactory.registerScope("request", requestScope);
      }
   }



Friday, January 29, 2010

GWT com.google.gwt.core.client.JavaScriptException: (null): null

I recently discovered this problem when doing GWT 2.0 tests in develoment mode. (Other reports of the problem.)
I've seen it arise in calls to constructors to GWT UiObjects/Widgets, such as FlexTables, when I restrict GWT to using only a particular browser, with a line in the module definition such as

<set-property name="user.agent" value="ie6">
Just remove that line, because the headless browser the htmltest framework uses is incompatible with IE6.

Glenn has more details.