Reputation: 1113
I am writing simple DSL in JRuby, for a Java library. Some object created in JRuby are passed to the Java code, processed and returned back to JRuby. I am using JRuby 1.7.8.
At some point while processing the objects created in JRuby need to be serialized and deserialized by the Java library (to perform deep cloning). Here is where the problem occurs. Appart from String and a plain Object it seems not possible to serialize in Java objects created in JRuby.
For example, while serializing to an empty Hash object I get:
IOError: org.jruby.RubyHash$RubyHashEntry
load at org/jruby/RubyKernel.java:1103
at /usr/local/rvm/gems/jruby-1.7.8/bin/pry:23
eval at org/jruby/RubyKernel.java:1123
(root) at /usr/local/rvm/gems/jruby-1.7.8/bin/jruby_executable_hooks:15
for [ "asd" ]
i get:
IOError: org.jcodings.specific.UTF8Encoding
load at org/jruby/RubyKernel.java:1103
at /usr/local/rvm/gems/jruby-1.7.8/bin/pry:23
eval at org/jruby/RubyKernel.java:1123
(root) at /usr/local/rvm/gems/jruby-1.7.8/bin/jruby_executable_hooks:15
I found some posts about it (e.g. here and here), but they are all quite old and do not explain anything about future plans. It seems to me an important limitation when considering integration of Ruby and Java.
Now I'm serializing my objects to JSON on Ruby side, and deserializing them from JSON when they are returned. It works, but this comes with significant efficiency penalty.
So the questions are:
EDIT:
This spec in JRuby repo suggests that there is something going on concerning the serialization. However, its still 2 years old and does not explain serialization policy of other objects.
Upvotes: 1
Views: 866
Reputation: 1113
The problem with deep cloning JRuby objects is that they include recursive references all the way up to the Ruby interpreter. So if you try to recursively clone an object you end up cloning the whole Ruby world and this must end up as a disaster... At least that's how it was in 2013.
As suggested by @tilpner I gave up cloning by serialization and started using this: https://github.com/kostaskougios/cloning. First advantage is efficiency -- it's much faster then serialization. Second and more important here is that you have much more control over what is being cloned and what is omitted. Thus, by trial and error I came to the following configuration that allowed for correct cloning of Ruby objects. I don't remember all details now, so I also include all my comments as found today in this code. Hope it will be helpful.
// prevent from cloning whole JRuby mess
cloner.dontClone(Class.forName("org.jruby.RubyClass"));
cloner.registerImmutable(Class.forName("org.jruby.RubySymbol"));
// WRz 2014-10-27
// prevent cloning the whole mess with JRuby Proc object
// FIXME: this does not copy bock's binding! That means that
// the cloned Procs share the same closure! Unfortunatelly
// the struggling to do it better (see below) gave no solution...
// Deep cloning the Block pulls an awful lot of stuff! I cannot handle this
// and this or the other way whole JRuby, java.reflection and more gets cloned...
cloner.dontClone(Class.forName("org.jruby.runtime.Block"));
// JRuby's active Enumerator caused cloning of whole mess without this:
cloner.dontClone(Class.forName("org.jruby.Ruby"));
/*
cloner.dontClone(Class.forName("org.jruby.MetaClass")); // singleton!?
cloner.dontClone(Class.forName("org.jruby.parser.LocalStaticScope"));
cloner.dontClone(Class.forName("org.jruby.ast.executable.RuntimeCache"));
cloner.dontClone(Class.forName("org.jruby.Ruby"));
cloner.dontClone(Class.forName("java.lang.reflect.Constructor"));
cloner.dontClone(Class.forName("org.jruby.javasupport.proxy.JavaProxyConstructor"));
*/
As you see that's how it was almost a year ago. Later I moved all my implementation to Ruby and was able to make it much more faster, so I lost track of this problem.
Upvotes: 1