" /> Alan's Ramblings: March 2005 Archives

« February 2005 | Main | April 2005 »

March 26, 2005

Morons on the moors

Well, I've just been on what must be the most depressing patrol of my time as a Ranger. Not only did the weathermen get it completely wrong and I got completely soaked, but I had the disheartening task of recording the serious and systematic vandalism of the new fence that has been put around Bleaklow as part of the attempt to regenerate the moorland. When I got to the Briefing Centre this morning, Fiona said that one of the local Gamekeepers had reported that a fence had been cut above Salter's Brook on the A628, and she asked me if I'd check it out. Sure enough, it was the new stock exclusion fence that had been chopped.

The vandalism started at Far Small Clough Head and I counted 38 places where the fence had been cut between there and Swain's Greave, a distance of about 2km - and that wasn't the end of it, I could see more cuts in the fence as hit headed east towards Barrow Stones. God knows how much further the damage goes but I'd run out of time and had to head for home. I temporarily fixed the first dozen or so sections, but whoever did it had quite clearly come prepared and has spent some considerable time performing their vandalism - the holes are every 50-100m, and they've cut the fence either side of two adjacent posts and entirely removed a section. In some places they've cut it by stiles, and in some places even next to the notices that explain that the fence is to keep stock out and not people. Needless to say, the sheep are already inside the fenced off area.

I'm at a loss to find the words to express my mixture of dismay and fury over what they've done - the damage is not just near footpaths, but right across the open moor. Whoever did this knew the area, had been before and came back with the tools necessary to destroy the fence. I'm sure the morons responsible feel that they were 'justified' in 'protecting their rights' despite the fact the land is actually privately owned and looked after. I'm also sure that if someone came and vandalised their property they'd be the ones baying for the blood of the offenders.

The fact of the matter is that because of their cretinous and downright criminal behaviour, everyone will lose:

  • This sort of behaviour undermines decades of careful liason with the landowners to establish access to the land - if it was my fence that had been destroyed I'd be making a strong case for people to be excluded completely from my land - and yes, despite many people's belief to the contrary, landowners can still get access removed - for example for nature conservation purposes.
  • The fence is there for a reason - to protect and therefore help to repair the moors, and that's now been jepoardised.
  • The damage will have to be repaired, and I estimate that there is at least 5-10 thousand pounds of damage - not only is there the cost of the materials, and the labour to put the fence up, there's also the small matter of having to hire a helicopter at 800 pounds per hour to fly it in as the terrain is too fragile for vehicle access.
  • The landowner is paid for maintaining the fence, and for excluding his stock - if the fence is cut, DEFRA stop paying the farmer, so he get's hit by a double whammy.
  • The repairs will probably have to be paid for by DEFRA, i.e, the government, i.e. by taxes, i.e. by you, me and indeed the prats who cut the fence in the first place.
Bah.

March 20, 2005

Benchmark your phone

I stumbled across a rather interesting website that provides benchmarks and test results of the Java implementations on a wide range of phones. Being an ex-benchmark engineer, this rather appealed to me ;-) The results can be found here. You are supposed to be able to download the MIDlet by browsing the WAP page at http://wap.club-java.com/en, but I had problems reading that page. The direct URL of the MIDlet is http://www.club-java.com/TastePhone/TastePhone.jad, and that worked fine for me.

March 19, 2005

First t-shirt patrol of the year

Today was really rather warm - in the mid to upper teens (mid 60's fahrenheit for any colonials listening in), so rather than being bundled up like an Inuit I got to walk around in my t-shirt - it looks like spring is really here at last. Unfortunately the warm weather also meant is was very hazy, so I didn't get any decent photos. However it's only a few weeks since we had some significant snowfall - the pictures below were taken on Kinder Low when we were airlifting at the beginning of the month.

I'm particularly fond of the one below - apart from correcting the blue colour cast and contrast problems you get with photos of snow, it's untouched.

March 18, 2005

Improving the look of fonts under Swing

As I said in my last post, I've spent quite a bit of time over the last few days fiddling with Java, and one of the things that bugged me is that the fonts look pretty crappy because they aren't antialiased. The only way to do this in JVM 1.4 and before is to subclass every widget and/or install your own PLAF (Pluggable Look And Feel) - there are a couple of projects which have done this, for example wraplf and smoothmetal. However, as of JRE 1.5, there's a way to do this globally for your application:

    System.setProperty("swing.aatext", "true");

Unfortunately this doesn't work inside an applet as writing to a system property is forbidden and you'll get a security exception. You can achieve the same effect by adding -Dswing.aatext=true to the command line. If you want to set this for all applets you load, open up the Java Plugin Manager, go to the Java tab and click on the View button under Java Applet Runtime Settings. In the table, click on the Java Runtime Parameters field for the 1.5 JRE and enter -Dswing.aatext=true as the value, then save and exit. Voila, all your applets will use antialiased fonts.

Two birds with one stone

Since my employer Sun Microsystems informed me that I was being made redundant last month I've been on so-called "gardening leave", and for the first time in a very long time I've had time to do stuff just for the hell of it. I decided I'd take a look at NetBeans, a IDE for Java that I'd heard good things about.

After grabbing the latest JDK, at Gary's suggestion I downloaded a copy of the Open Source NetBeans IDE to have a play. I've never been a particular fan of IDEs, but NetBeans is actually very good. As well as the editor and debugger there's also GUI designer and a load of other bits, and it all works together very well. I particularly like the 'as you go' syntax checking which highlights errors in your code in much the same way as the auto spellchecker works in a word processor - the erroneous code is underlined in red, and if you move the cursor over the line you get a diagnostic message. The editor also support folding, something I first saw years ago in the Occam editor.

The next job was to think of something small but useful to write. Although I've already deployed some anti-blogspam measures on this site, I'm beginning to notice a gradual increase in attacks - inevitably the spammers are getting wise to the more common tricks used to put them off. Some countermeasures such as the "answer this maths question" approach used by blogs.sun.com are trivially circumventable. The most popular and sucessful countermeasure at the moment seems to be to use a captcha, but personally I don't like them as I feel they are intrusive, and despite the hype about them the implementations often have flaws that still leave them open to attack. The problem is that HTTP is a stateless protocol, so each page has to contain enough context to enable the server to verify that the response to the captcha is correct, whether that be a hidden form field, a cookie or whatever. Because of that, any such scheme is vunerable to capture/replay attacks. Even using HTTPS to encrypt the communication channel doesn't protect against the attacker viewing the page and/or cookie source and figuring out the protection mechanism.

I therefore decided that obfustication of the communication between the webserver and browser was probably a reasonable approach, and one way of doing this was to implement comment submission using a Java applet. However MovableType uses HTML forms and HTTP POSTs requests to submit comments, and as I didn't want to rewrite the back-end I had to figure out how to get a Java applet to behave as if it were a HTML form.

The first step was to figure out how to obfusticate the form contents so that it wouldn't be possible to figure out by inspecting the HTML which form fields were required to submit a comment. To do this the CGI script on the server needed to send out the form with one set of field names, and the browser needed to submit the form back to the server with a different set of names. In addition I wanted to change the names from the default MovableType names and hide the comment submission URL so that existing attack scripts wouldn't work. To do this I embeded a non-visible form into the comment submission page - I made it invisible by making all the individual fields hidden:

    <form id="CommentForm" name="CommentForm" method="" action="">
    <input type="hidden" name="entry_id" value="9999" />
    <input type="hidden" name="input1" value="" />
    <input type="hidden" name="input2" value="" />
    <input type="hidden" name="input3" value="" />
    <input type="hidden" name="input4" value="" />
    <input type="hidden" name="submit1" value="" />
    </form>

Notice also that the action attribute of the form is empty so you can't tell from the HTML the URL of the comment submission script. It is filled in by the applet when the submit button is pressed, along with the names and values of the hidden fields. The next bit of the jigsaw was to write the applet to display the form - a snip using the NetBeans GUI builder - and to figure out how to access the HTML of the containing page from the applet, which was a little harder. It turns out there are two possible ways to do this. The first is by calling back from Java into Javascript to obtain information from the browser (Netscape called this mechanism LiveConnect, it's also known as JSObject). Documentation for the API can be found in the Java plugin developer's guide. The second method us by direct DOM access via the Common DOM API. However the JSObject mechanism is older and appears to have better browser support - a bit of googling revealed that the Common DOM API was pretty flaky, and rather than providing a get/set interface like the JSObject API it requires you to access the DOM using a callback mechanism using a subclass of DOMAction - yuck. Needless to say, I used the JSObject mechanism. In order to use it it's necessary to enable it by adding the mayscript attribute to the applet used to load up the applet:

    <applet id="CommentApplet" class="CommentApplet"
    codebase="/blog/java/CommentApplet.jar"
    code="CommentForm.class" width=350 height=350 mayscript="true">
    </applet>

In order to use the JSObject methods to manipulate the form's fields it's first necesary to get handles to the appropriate entity within the enclosing HTML document:

    JApplet myApplet;
    :
    JSObject obj = JSObject.getWindow(MyApplet);
    JSObject doc = (JSObject) obj.getMember("document");
    Object args = new Object[1];
    args[0] = "CommentForm";
    JSObject form = (JSObject) doc.call("getElementById", args);

Having done that it is easy to manipulate the form, for example here's how the method and action attributes are set:

    String method, action;
    :
    form.setMember("method", method);
    form.setMember("action", action);

Sending the form back to the webserver requires that we call the JavaScript submit method on the form, like this:

    Object[] args = new Object[0];
    form.call("submit", args);

I made the corresponding changes to the MovableType lib/MT/App/Comments.pm module to map the form field names to/from the obfusticated versions, changed the MovableType comments templates to contain the new form and applet tag definitions and everything worked as I expected. However the applet looked significantly different from the rest of the comment page - the font and foreground/background colours were all different from the rest of the page, and the buttons looked like the standard Java ones instead of normal HTML form ones. A little googling solved the problem - Java has pluggable "look and feel", although it's poorly documented. The relevant documentation is the UIManager and PLAF classes. It's possible to tell Java to use the platform L&F like this:

    javax.swing.UIManager.setLookAndFeel(
        javax.swing.UIManager.getSystemLookAndFeelClassName());

which makes the buttons and any dialogs adopt the platform L&F rather than the standard Java one. The next thing was to see if it was possible to make the applet stylesheet-aware as well, so that it picked the fonts and colours up from the enclosing page. My first thought was to grab the stylesheet entry associated with the block that the applet was in, but it became apparent that approach wouldn't work - style attributes are inherited, so the actual attributes used to render a block are the merge of the explicit style attributes specified for the block, plus any inherited from the parent blocks. What we actually need to do is to get the calculated attributes. Unfortunately, cross-browser portability rears it's ugly head at this point - Mozilla/Firefox and Internet Explorer have different mechanisms to do this, Firefox uses a call to getComputedStyle() to get the style object, which can then be queried with getPropertyValue() whereas IE uses an attribute of the object called currentStyle to hold the style information. In addition, the format of the attribute names is different in the two browsers - Firefox expects normal CSS hyphenated attribute names, e.g. background-color whilst IE requires them to be camelCased, e.g. backgroundColor. There are other differences as well - Firefox returns colours as a string of the form rgb(10,20,30) whilst IE returns them as a colour name, e.g. red or as a HTML colour code, e.g. #102030.

The last wrinkle is to do with font sizes, which are quite frankly a complete mess when you are trying to get predictable cross-platform behaviour. There are whole slew of issues:

  • Internet Explorer returns a relative font size if that's how the font size was specified in the stylesheet, e.g. medium instead of a pixel or point size, so it's impossible to know what point size this actually corresponds to.
  • In Firefox when you query the parent document style object for the font size you get a value back in pixels. Java however expects font sizes to be specified in points, so you would expect to have to scale the pixel value by the display resolution.
  • Windows doesn't use the real DPI value of the display, it uses one of two fixed sizes, 96 DPI (the "small fonts" display setting) or 120 DPI (the "large fonts" option). Querying the display resolution form Java (using Toolkit.getScreenResolution()) therefore returns a fake value.
  • There are a number of long-standing bugs/quirks in the font size handling in Java pre-JRE 1.5 - basically Java assumes that all displays have a pixel pitch of 72 DPI, irrespective of the actual resolution.
The easiest way of dealing with this mess was to encapsulate the differences in two classes:

    // Interface for CSS readers.
    private interface CssReader {
        String getAttr(String name);
        int parseFontSize(String size);
        Color parseColor(String color);
    }

    // Class to read Mozilla CSS.
    private class MozillaCssReader implements CssReader {

        public MozillaCssReader(JSObject doc, JSObject applet) {
            JSObject obj = (JSObject) doc.getMember("defaultView");
            Object[] args = new Object[2];
            args[0] = applet;
            args[1] = new String("");
            style = (JSObject) obj.call("getComputedStyle", args);
            fontSizeRE = Pattern.compile("\\A([\\d.]+)(p[tx])\\z",
              Pattern.CASE_INSENSITIVE);
            rgbRE = Pattern.compile(
              "\\Argb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)\\z",
              Pattern.CASE_INSENSITIVE);
        }
        
        public String getAttr(String name) {
            Object[] args = new Object[1];
            args[0] = name;
            return((String) style.call("getPropertyValue", args));
        }

        public int parseFontSize(String size) {
            System.out.println(size);
            Matcher m = fontSizeRE.matcher(size);
            if (m.matches()) {
                // Java assumes the display is 72 DPI,
                // so "px" and "pt" sizes are equivalent.
                return((int) (Float.parseFloat(m.group(1)) + 0.5F));
            } else {
                return(defaultFontSize);
            }
        }

        public Color parseColor(String color) {
            if (color.equals("transparent")) {
                return(new Color(0xFF, 0xFF, 0xFF, 1));    
            } else {
                Matcher m = rgbRE.matcher(color);
                m.matches();
                return(new Color(Integer.parseInt(m.group(1)),
                  Integer.parseInt(m.group(2)),
                  Integer.parseInt(m.group(3))));
                }
            }

        private JSObject style;
        private Pattern fontSizeRE;
        private Pattern rgbRE;
    }

    // Class to read Explorer CSS.
    private class ExplorerCssReader implements CssReader {

        ExplorerCssReader(JSObject doc, JSObject applet) {
            style = (JSObject) applet.getMember("currentStyle");
            attrRE = Pattern.compile("-[a-z]");
            sizeToPoint = new HashMap();
            sizeToPoint.put("xx-small", new Integer(6));
            sizeToPoint.put("x-small", new Integer(8));
            sizeToPoint.put("small", new Integer(10));
            sizeToPoint.put("medium", new Integer(12));
            sizeToPoint.put("large", new Integer(14));
            sizeToPoint.put("x-large", new Integer(18));
            sizeToPoint.put("xx-large", new Integer(24));
            ss = new StyleSheet();
        }
        
        public String getAttr(String name) {
            Matcher m = attrRE.matcher(name);
            StringBuffer nm = new StringBuffer();
            while (m.find()) {
                String r = m.group().substring(1).toUpperCase();
                m.appendReplacement(nm, r);
            }
            m.appendTail(nm);
            return((String) style.getMember(nm.toString()));
        }

        public int parseFontSize(String size) {
            Object sz;
            if ((sz = sizeToPoint.get(size)) != null) {
                return(((Integer) sz).intValue());
            } else {
                return(defaultFontSize);
            }
        }

        public Color parseColor(String color) {
            if (color.equals("transparent")) {
                return(new Color(0xFF, 0xFF, 0xFF, 1.0F));    
            } else {
                return(ss.stringToColor(color));
            }
        }

The last step is to figure out which browser the applet is running in. As IE doesn't have the getPropertyValue() method, the easiest way is to try to fetch it and trap the resulting exception we get when running under IE:

    CssReader css = null;
    try {
        form.getMember("getPropertyValue");
        css = new MozillaCssReader(doc, applet);
    } catch (JSException e) {
        css = new ExplorerCssReader(doc, applet);
    }

Phew. If you want to see it all in action, follow the "Comments" link below. It's not perfect as I've had to work around several misfeatures as described above, but it does work, at least with Firefox and IE. I've had a report that it doesn't work in Safari - if any Safari users out there can send me the Java Console stack trace they get when it fails, I'll try to fix it!