0

I am trying to create a serializable interface implementation in groovy dynamically which could be send over the wire where it can be deserialized and executed with args. I have created anonymous interface implementation using map but it fails on serialization.

gcloader = new ​GroovyClassLoade​r()
script = "class X { public def x = [call: {y -> y+1}] as MyCallable }"​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
gclass = gcloader.parseClass(script)
x = gclass.newInstance().x​​
// serialzing x fails

I am not sure if a groovy closure is compiled to a random class name, which would make it impossible to deserialized even if it gets serialized. Is there a way to do this?

FUD
  • 5,114
  • 7
  • 39
  • 61
  • 2
    What's the exception? – tim_yates Nov 05 '14 at 14:39
  • I am using https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/SerializationUtils.html and strangely I get java.io.NotSerializableException: X i.e. the top level class :( – FUD Nov 05 '14 at 14:42
  • Actually if i do "X implements Ser.." then i am able to serialize it but on deserialzing i get java.lang.ClassNotFoundException: X$_closure1, which makes sense. So can this be done by some other trickery? – FUD Nov 05 '14 at 14:45
  • Maybe you need to use your [custom class loader](http://stackoverflow.com/a/13876075/563890) `gcloader` on deserializing? – Will Nov 05 '14 at 16:37

2 Answers2

0

Here's a piece of code that might be helpful:

import groovy.lang.GroovyClassLoader

def gcLoader = new GroovyClassLoader()
def script = """
class X implements Serializable { 
    public def x = [ 
        call: { y -> y + 1 } 
    ] 
}"""
def cls = gcLoader.parseClass(script)
def inst = cls.newInstance().x

def baos = new ByteArrayOutputStream()
def oos = new ObjectOutputStream(baos)

oos.writeObject(inst)

def serialized = baos.toByteArray()

def bais = new ByteArrayInputStream(serialized)
def ois = new CustomObjectInputStream(bais, gcLoader)
inst = ois.readObject()

assert 2 == inst.call(1)

public class CustomObjectInputStream extends ObjectInputStream {
    private ClassLoader classLoader

    public CustomObjectInputStream(InputStream ins, ClassLoader classLoader) throws IOException {
        super(ins)
        this.classLoader = classLoader
    }

    protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
        return Class.forName(desc.getName(), false, classLoader)
    }
}

Basically, You need an instance of ObjectInputStream with custom ClassLoader.

Opal
  • 81,889
  • 28
  • 189
  • 210
  • thanks @Opal, but do you think this would work when i send the serialized object over wire? On the other side even if i have a classloader it would not have those classes (e.g. closure) defined for serialization. – FUD Nov 06 '14 at 03:42
  • No idea really. You need to do further investigation. – Opal Nov 06 '14 at 03:54
0

According to my own limited research I came to conclusion that, In jvm there is no standard/popular library which could pickle code like in python which was the requirement i was primarily after. There are some ways to do it through URL classloaders etc but comes with some inherent complexities. I ended up just simply sending the code string and recompiling it whenever required on multiple machines.

FUD
  • 5,114
  • 7
  • 39
  • 61