Archive

Archive for the ‘Internals’ Category

Where did that Class come from?

April 21, 2013 Leave a comment

Too many times I watched the logs trying to understand why I got the NoSuchMethodError. I sometimes get it with other derivatives of IncompatibleClassChangeError (AbstractMethodError, IllegalAccessError, InstantiationError, NoSuchFieldError) but NoSuchMethodError is the most common one.

The reason for this error is simple – the class you want to use is not the one being loaded at run time. This can be another version of the third party that finds its way to the classpath, or some old copy of your class. The problem is usually pin-pointing where it got from.

For that I have a simple solution – which unfortunately requires some code modification.
All you need to do is add these lines to your code (modify slightly) and you will get the result:

String className = classYouWantToFind.getName();
String classFileRelativePath = className.replace('.', '/') +  ".class";
java.net.URL classFileUrl = this.getClass().getClassLoader().getResource(classFileRelativePath);
System.out.println(className + " comes from [" + classFileUrl.toString() + "]");

This code asks the ClassLoader of the currently running object to find the .class representing the class you are looking for. Since the class loader searches for resources in the same order as it does for classes – it should find the correct one.

You can see a live example of this code on IDE One.

Note: You can always get this result by adding the -verbose:class VM argument – but this will get you verbose output of every class in the system.

Advertisements
Categories: Internals, Java Tags: ,

RTFM

August 4, 2011 Leave a comment

I recently encountered a little piece of code which reminded me why it is important for a developer to RTFM.

I saw in some code base the following utility method:

public static String allowOnlyNumbers(String input)
{
   StringBuilder sb = new StringBuilder(input.length());
   for (int i=0; i<input.length(); i++)
   {
      char c = input.charAt(i);
      if (Character.isDigit(c))
      {
          sb.append(c);
      }
   }
   return sb.toString();
}

This utility method was used in the following context:

...
PhoneDetails phone = new PhoneDetails();
phone.setNumber(allowOnlyNumbers(phoneNumber));
phone.setAreaCode(allowOnlyNumbers(areaCode));
...

So the method had to only allow characters in the range of ‘0’-‘9’. Looks OK, right?
I ran a quick test:

System.out.println(allowOnlyNumbers("abc123"));

The result was as expected:

123

I always look at Java Core’s sources to learn, so I looked at isDigit(char) as well to see how they did it, and to my initial surprise, the code was NOT:

public boolean isDigit(c)
{
   return c>='0' && c<='9';
}

It was far more complex and seemed to support Unicode as well. So I ran another test on the code:

System.out.println(allowOnlyNumbers("\u06F1\u06F2\u06F3")); // Arabic digits 1, 2 and 3

The result was again as expected (but not as intended):

۱۲۳

Needless to say, looking at the JavaDoc it is specified very clearly how this method behaves:

Determines if the specified character is a digit.

A character is a digit if its general category type, provided by Character.getType(ch), is DECIMAL_DIGIT_NUMBER.

Some Unicode character ranges that contain digits:

‘\u0030’ through ‘\u0039’, ISO-LATIN-1 digits (‘0’ through ‘9’)
‘\u0660’ through ‘\u0669’, Arabic-Indic digits
‘\u06F0’ through ‘\u06F9’, Extended Arabic-Indic digits
‘\u0966’ through ‘\u096F’, Devanagari digits
‘\uFF10’ through ‘\uFF19’, Fullwidth digits

Many other character ranges contain digits as well.

So the message here is (as Baz Luhrmann said):

Read the directions even if you don’t follow them.

You can see the code running here

Knowing if a class you received is compiled for Java5 or Java6

April 28, 2011 Leave a comment

Working in a multi version environment I found my self quite often receiving a .class file compiled with the wrong compiler version. Usually receiving a .class file compiled with JDK6 whereas I was working in an environment running with JRE5.

The problem is quite simple to understand when you get it during run-time, but unfortunately you sometimes only discover it after giving the customer a hot-fix for a problem only to discover you compiled with the wrong settings. Or sometimes just to test your code you need to bounce the file between several ftp sites just to get it to the run-time environment.

So I sat a few minutes and analyzed the “header” of the binary .class file and found a simple way to know if it was compiled for Java5, or for Java6.

Open it in any HEX viewer/editor and you can see the answer:

The 8th bit (byte #7 in HEX view) is equal to 0x31 for Java5 and 0x32 for Java6.
Just a note: 0x31
=49 and 0x32=50, and 49 & 50 are the major versions for Java5 & Java6

Below you can see samples of the beginnings of two .class files:

Sample Java 5 .class file:

Sample Java 6 .class file:

It is important to mention that the above is a bit inaccurate but it is more than enough for the existing versions of Java and for the problem at hand.
The accurate definition of the .class file’s header is as such (taken from the Java 2nd edition Spec):

  • Bytes 0-3: Magic Number (Value is always – 0xCAFEBABE)
  • Bytes 4-5: Minor Version (Stored in big-endian format)
  • Bytes 6-7: Major Version (Stored in big-endian format)
    • So byte #7 (the 8th byte) is the less important byte of the 16bit unsigned integer that represents the Major Version. So when we reach Major Version 256 – the 8th byte will not be enough and I’ll have to revise this post.
Categories: Internals, Java Tags:
%d bloggers like this: