Extensions and Deserialisation

When loading classes as extensions in a NetBeans Platform application, serialising the dynamically loaded objects is straight forward. However, deserialising them is not. 🙂

This article assumes that the reader is familiar with extension points. For more information on this topic, read the NetBeans Platform 7.1 Quick Start tutorial, the Lookup API javadocs and Toni Epple’s article How Do NetBeans Extension Points Work? (Note that the article was written before the @ServiceProvider annotation was introduced.) And for a general code example for serialisation, see Serialize an object to a file.

As an example of the problem, consider the following scenario. In a module called VehicleProvider, I have an interface called IVehicle. This is the interface that extensions need to implement. And I have a class called VehicleProvider that loads all the instances of IVehicle and keeps them in a serialisable collection (called mList). In a separate module called Car, I have a class called Car that implements IVehicle and Serializable, and is registered as providing the IVehicle service.

VehicleProvider does not have a dependency on module Car, but loads dynamically the object that is found in it using Lookup.

Serialising the collection in the VehicleProvider class works perfectly as expected:

try
{
    ObjectOutputStream out = new ObjectOutputStream(
            new FileOutputStream("f.dat"));
    out.writeObject(mList);
    out.close();
}
catch (FileNotFoundException ex)
{
    Exceptions.printStackTrace(ex);
}
catch (IOException ex)
{
    Exceptions.printStackTrace(ex);
}

However, when deserialising the same file, things go wrong:

try
{
    ObjectInputStream in = new ObjectInputStream(
            new FileInputStream("f.dat"));
    mList = (ArrayList) in.readObject();
    in.close();
}
catch (ClassNotFoundException ex)
{
    Exceptions.printStackTrace(ex);
}
catch (FileNotFoundException ex)
{
    Exceptions.printStackTrace(ex);
}
catch (IOException ex)
{
    Exceptions.printStackTrace(ex);
}

When readObject() is called, a java.lang.ClassNotFoundException is thrown indicating that class Car is nowhere to be found. And if you think about it, that is entirely true. Each module in a NetBeans Platform application has its own class loader and hence its own class path. The class path is determined by the module dependencies. And since no dependency exists on module Car, the VehicleProvider module’s class loader would not know about the class.

After much Googling and debugging through source code, I realised that I needed a way to resolve the classes. And so I extended ObjectInputStream for this purpose:

public static class SystemObjectInputStream 
        extends java.io.ObjectInputStream
{
    public SystemObjectInputStream(InputStream TheStream) 
            throws IOException, StreamCorruptedException
    {
        super(TheStream);
    }

    @Override
    protected Class resolveClass(ObjectStreamClass Osc) 
            throws IOException, ClassNotFoundException
    {
        Class theClass = null;

        ClassLoader syscl = Lookup.getDefault().lookup(ClassLoader.class);
        theClass = Class.forName(Osc.getName(), true, syscl);

        return theClass;
    }
}

And in the deserialisation code, the derived class is used instead of ObjectInputStream:

in = new SystemObjectInputStream(new FileInputStream("f.dat"));

The important part of the SystemObjectInputStream code above is the line where Lookup is used to find an instance of ClassLoader.class. This is the system class loader, and it has access to all the modules in the application.

Leave a Comment