Puzzler: the catalyst method does not work

Recently I wrote a post about a catalyst method in Java – an issue where an existence of a method that is never actually invoked can affect the code execution. The trick was that the way compiler created bytecode for static method was ambiguous for this given code snippet and therefore JVM resolved it incorrectly.

The code snippet I presented before publishing was simplified multiple times to make is as readable as possible. At some point the following simplification was suggested:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
public static class BaseSerializer {
    static <E> Object serialize(E obj) {
        return "BaseSerializer " + obj.toString();
    }
}

public static class CatalystSerializer extends BaseSerializer {
    static <E> String serialize(E obj) {
        return "CatalystSerializer " + obj.toString();
    }
}

public static class AdvancedSerializer extends CatalystSerializer {
    static <E extends Serializable> String serialize(E obj) {
        return "AdvancedSerializer " + obj.toString();
    }
}

public static void main(String[] args) {
    System.out.println(AdvancedSerializer.serialize("ABC"));
    System.out.println(AdvancedSerializer.serialize(new Object()));
}

…but did not worked – the bug was missing. So that is a puzzler: why the snippet above works fine while the code in a previous post did not (see the code for comparison). Where is the difference that made the issue appear its ugly head?

Before I give away the answer let me say what did not caused the difference. It’s obviously not because of the classes names I used (hahaha!) or the content of each of the methods. It is not because of using a different interface – Serializable in this code does the same thing as Comparable in the previous one. Finally it is not because of using Lists or any other collections – no matter how many flaws they might have it is not what contributed to the puzzle. So what is it?

If you read the previous post it won’t be a surprise to you: it’s because of type erasure. In original code the JVM got confused because two different for compiler static methods were saved as the same bytecode. This was possible due to type erasure – the generic typing was the only difference between those methods and that got ‘lost in translation’.

The situation in the code above is slightly different: you might expect that after type erasing both calls in main to AdvancedSerializer.serialize() and CatalystSerializer.serialize() will look the same and JVM will make an error again. In fact the first one will get compiled to ‘String AdvancedSerializer.serialize(Serializable)’, while second one due to a compiler brilliance will become ‘String AdvancedSerializer.serialize(Object)’!

What’s the lesson here? Well, puzzle itself is quite interesting but… I just can’t think of any :) Maybe only this: it’s good to have brilliant Java compiler, huh?

JConsole: analyze your Java apps

Today we will show a pretty useful program which is a part of Java JDK but whose existence is not known to many Java programmers. It’s named jconsole and it is a GUI client for accessing Java Management Extensions (JMX). In short, it allows you to view a lot of data about memory usage, running threads, garbage collector performance and much more other useful runtime informations about any Java process running on your machine.

The program itself is part of Java JDK 1.6. It is extremely easy to run and use. If you are running a Linux system with JDK 1.6 installed, all you have to open the terminal and type:

jconsole

On a Windows system it should also work by running it from the Command Line, assuming you have your PATH value properly set to your JDK’s bin directory. Otherwise you should find the jconsole.exe file in ‘C:\Program Files\Java\jdk1.6.0_16\bin’ directory (change the jdk1.6.0_16 to directory name your JDK is installed in).

Either way, after running it you will see the cool ‘New Connection’ dialog window:

jconsole start

This is where you can specify which Java process you want to connect to. Because the JConsole is itself a Java process, you see it too on the ‘Local Process’ list. If any other Java programs were running on your system, they would be automatically discovered and you would see them on the list too, under some other PID. There is also a ‘Remote Process’ option that allows you to connect to a Java process running on another machine (this requires more configuration though).

For the simplicity let’s connect now to JConsole process itself. Click ‘Connect’ and wait for like 3 seconds while the JConsole is trying to connect. After it succeeds, you will be moved to the ‘Overview’ screen which shows a few graphs about memory usage, CPU, Loaded classes and so on:

jconsole overview

To put it clear: in this situation the JConsole program is connected to itself and the displayed data are of the JConsole Java process. Normally you would not use it like that, instead you would connect it to some real, separate program, for example a Java EE server or any other running Java application.

All the graphs you can see in the JConsole can be clicked on with the right mouse button in order to save their data as a CSV file. Besides ‘Overview’ there are also other tabs, like ‘Memory’ and ‘MBeans’. In the ‘Memory’ tab you can see a lot of information about how Garbage Collector works and distribution of data in memory. You can choose any of the memory regions by clicking one of the green rectangles on the bottom right and a detailed graph will be displayed:

jconsole memory

Since JConsole is a client for the JMX services, you could use it to access your own MBeans. They are special kind of classes (kind of Java Beans with getters and setters) useful for gathering informations about resources you care about. All the graphs you can see in the JConsole are provided by some standard set of MBeans registered by default when you run any Java application. You can write and register also your own MBeans. There is a nice tutorial about it available on Sun’s website.

How equals() works for URLs

If you ever were working on a Java application that had anything to do with the web you are probably familiar with java.net.URL class. I have a puzzle for you then: let’s say in your application you refer to JavaBlogging site with two URL using slightly different host name. Will those URL objects be equal to each other? See the following code snippet:

1:
2:
3:
4:
5:
6:
7:
public static void main(String[] args) throws Exception {

    URL url1 = new URL("http://www.javablogging.com");
    URL url2 = new URL("http://javablogging.com");

    System.out.println(url1.equals(url2));
}

So what will get printed out? One could argue that those links refer to the same site so we should get ‘true’… but on the other hand the address spelling is different so maybe the correct answer is ‘false’? You may not be decided which solution is the right one, but since its a deterministic code we know at least that one of them is correct… right?

Wrong! You may get either ‘true’ or ‘false’. The strange thing is that the result does not depend on JVM, on your machine specifics or Java version, but… on whether or not you are connected to the Internet!

This strange behavior is explained partially in URL JavaDoc (Doh!): one of the conditions for the two URLs to be considered equal is that the host names are resolved to the same IP. Therefore first step in checking for equality is resolving the IP address and this can be done only with Internet connection. Without it the host names are compared as Strings.

What are the consequences of this behavior, besides all of the confusion? One thing is that because of the IP resolution the equals() method for URL is very slow. What is even worse the equals() method is inconsistent and therefore breaking the Object.equals() contract! (see the requirements for equals() method for comparison)

Now check out the following ‘improvement’ to the code:

1:
2:
3:
4:
5:
6:
7:
8:
9:
public static void main(String[] args) throws Exception {

    URL url1 = new URL("http://www.javablogging.com");
    URL url2 = new URL("http://javablogging.com");

    while (true) {
        System.out.println(url1.equals(url2));
    }
}

disconnect yourself from Internet, run the code, connect again and enjoy URL changing its mind about the result of equality!

So what is the lesson from all of this? Remember that URL is cheating and its implementation of equals breaks the contract stated in Object class. Therefore URL should never be used in situations when it can be tested for equality as the results may be unpredictable. Beware of using it with collections: not use it as a Map key or as an element of Set or you may get yourself in trouble…