AnsweredAssumed Answered

Classloader trouble while deserializing process variables

Question asked by danielbehrwind on Nov 11, 2011
Latest reply on Nov 12, 2011 by trademak
Hi,

I'm using activiti in a Grails project and having trouble when it comes to deserializing Groovy objects that are stored in process variables. I'm experiencing pretty much the same problems as described in this post (not using the Grails Avticiti plugin, though): http://code.google.com/p/grails-activiti-plugin/issues/detail?id=16
To sum it up, deserializing my objects fails with a ClassNotFoundException, which is due to the wrong ClassLoader being used.

I came up with a workaround employing the suggested solution from http://jira.codehaus.org/browse/GROOVY-1627, which is to subclass ObjectInputStream and overriding the resolveClass method in order to use a specific class loader (in my case the grailsApplication.classLoader). The workaround seems to work but it is rather ugly and I am wondering, if there is a better solution to my problem.

Here's my approach: In order to tap into the process of deserializing process variables, I subclass the Activiti SerializableType and override the getValue method, which is the place where deserialization happens. I basically copy-pasted the SerializableType's implementation an replaced the ObjectInputStream with my custom implementation. During application startup I register my custom SerializableType with the ProcessEngineConfiguration.
Here's my Grails service that does all that:

class CustomSerializableProcessTypeService {

    static transactional = false

    ProcessEngineConfigurationImpl processEngineConfiguration
    def grailsApplication

    void registerCustomSerializableType () {
        processEngineConfiguration.variableTypes.addType (new CustomSerializableType (), 0)
    }

    class CustomSerializableType extends SerializableType {
        @Override
        Object getValue (ValueFields valueFields) {
            Object cachedObject = valueFields.getCachedValue ();
            if (cachedObject != null) {
                return cachedObject;
            }
            byte[] bytes = valueFields.getByteArrayValue ()?.getBytes ();
            ByteArrayInputStream bais = new ByteArrayInputStream (bytes);
            Object deserializedObject = null;
            try {
                ObjectInputStream ois = new CustomObjectInputStream (bais)  // <=== This is the line of interest
                deserializedObject = ois.readObject ();
                valueFields.setCachedValue (deserializedObject);

                if (valueFields instanceof VariableInstanceEntity) {
                    Context.
                          getCommandContext ().
                          getDbSqlSession ().
                          addDeserializedObject (deserializedObject, bytes, (VariableInstanceEntity) valueFields);
                }

            } catch (Exception e) {
                log.warn ("exception occurred in getValue of custom SerializableType, trying super class", e)
            } finally {
                IoUtil.closeSilently (bais);
            }


            if (deserializedObject) {
                return deserializedObject;
            }
            return super.getValue (valueFields)
        }
    }

    class CustomObjectInputStream extends ObjectInputStream {

        CustomObjectInputStream (InputStream inputStream) {
            super(inputStream)
        }

        protected Class resolveClass (ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
            return Class.forName (objectStreamClass.getName (), true, grailsApplication.classLoader);
        }
    }
}

So here are my questions:

1. Is there a better way fix the ClassLoader issue?

2. If not, is there a better way to register custom VariableTypes? I'm currently using the ProcessEngineConfigurationImpl's VaraibleTypes object, which isn't even exposed by the ProcessEngineConfiguration interface. (By the way, I'm having a hard time to find documentation on this.)

I appreciate any hints!

Thanks a lot
Daniel

Outcomes