sixlegs.com / blog / java / skipping-constructors.html

Root Beer Logo Root Beer

Chris Nokleberg's Fizzy Weblog

August 2004
Su M Tu W Th F Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 26 27 28
29 30
Previous  |  Next  |  More...
#  Skipping constructor invocation

One common problem that CGLIB users run into is that there is no way to prevent the code in the superclass' constructor from being run. It is especially annoying for mocking purposes, as some classes will even throw exceptions from their constructors.

This is such a fundamental JVM limitation--if you don't emit bytecode to invoke the super constructor the verifier will complain--that I really didn't think there was any hope of getting around it.

Luckily there are some more optimistic developers out there, including Joe Walnes, who for his XStream project actually hooked into the JVM-specific com.sun.* classes to construct objects without a default constructor. I didn't want to be in the business of maintaining different code for every JVM version, but while chatting on #codehaus Joe suggested it might be possible to leverage Java serialization to do the same thing.

So, here is some code that constructs a serialized bytestream manually, in order to generate a deserialized instance without invoking the constructor:

import java.io.*;

public class Test
{
    public static void main(String[] args)
    throws IOException, ClassNotFoundException
    {
        Bar bar1 = new Bar();
        System.err.println("bar1: " + bar1.x);

        // should calculate suid using reflection on Bar.class
        long suid = -8713982946592877696L;
        Bar bar2 = (Bar)readObject(getSerializedBytes("Bar", suid));
        System.err.println("bar2: " + bar2.x);
    }

    private static byte[] getSerializedBytes(String className, long suid)
    throws IOException
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream data = new DataOutputStream(baos);
        data.writeShort(ObjectStreamConstants.STREAM_MAGIC);
        data.writeShort(ObjectStreamConstants.STREAM_VERSION);
        data.writeByte(ObjectStreamConstants.TC_OBJECT);
        data.writeByte(ObjectStreamConstants.TC_CLASSDESC);
        data.writeUTF(className);
        data.writeLong(suid);
        data.writeByte(2);  // classDescFlags (2 = Serializable)
        data.writeShort(0); // field count
        data.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA);
        data.writeByte(ObjectStreamConstants.TC_NULL);
        return baos.toByteArray();
    }

    private static Object readObject(byte[] bytes)
    throws IOException, ClassNotFoundException
    {
        ObjectInputStream in = 
          new ObjectInputStream(new ByteArrayInputStream(bytes));
        return in.readObject();
    }
}

The Bar class is simply:

public class Bar 
implements java.io.Serializable 
{
    public int x;

    public Bar() 
    {
        x = 666;
    }
}

The output is:

bar1: 666
bar2: 0

As you can see, the constructor code was skipped for the second instance.

Unfortunately this only works for classes that implement the java.io.Serializable marker interface. I'm sure this is for valid security reasons, but it is disappointing nonetheless. I haven't decided whether it is worth adding support for this to CGLIB, since often one won't have control over the superclass being proxied.

[Powered By FreeMarker]  [Valid Atom 1.0]  [Weblog Commenting by HaloScan.com]