1

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:

  1. Is it possible to somehow make Ruby objects Serializable in Java? If not, will it be possible in near future?
  2. Does anybody now a better (faster) workaround then the JSON solution I desribed above?

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.

wrzasa
  • 1,113
  • 10
  • 20
  • 1
    Ouch, cloning using serialization sounds painful. I know nothing about Ruby, but maybe just code it yourself? Like, recursively returning a clone of all data with reflection (still painful)? Still that should be a lot faster. – tilpner Dec 13 '13 at 20:29
  • The cloning is in Java, not in Ruby. This is a completely different discussion, but the serialization is how it is done. The solution with serialization is recommended for a long time. See page 45 in this book (for old reference): https://github.com/FelixRovinVincent/reference/blob/master/Design%20Patterns/The%20Design%20Patterns%20Java%20Companion.pdf or e.g. this question: http://stackoverflow.com/questions/64036/how-do-you-make-a-deep-copy-of-an-object-in-java – wrzasa Dec 13 '13 at 21:34
  • 1
    It may be recommended by some people, but it still feels disgusting to even consider (excuse me, it's just weird) this in my program/app. You may wan't to have a look at [this project](http://code.google.com/p/cloning/). – tilpner Dec 13 '13 at 21:37
  • Yes, I've just hit that and also this: http://www.matthicks.com/2008/05/fastest-deep-cloning.html -- very interesting too. Maybe it is a way to get out of the serialization problem while cloning. I must reconsider this. Thanks a lot, Your comment put me on a new track, I would certainly not consider myself. – wrzasa Dec 13 '13 at 21:45
  • Unfortunately, the cloner project: http://code.google.com/p/cloning/ mentioned in the comment above by @StackOverflowException seems incompatible with JRuby. If I try to deepClone a custom JRuby object in Java using this library JVM crashes. The same happens e.g. for most Hashes... anything that is not immutable... – wrzasa Jan 03 '14 at 21:39
  • @wrzasa did you find a palatable solution to this? I'm having similar problems trying to serialize RubyObject using HazelCast. Can't find any sensible solutions - would like to avoid json if possible – Ash McConnell Sep 14 '15 at 09:49
  • @AshMcConnell As you asked I added an answer since it's a little bit too long for a comment. I hope it will be helpful. – wrzasa Sep 14 '15 at 17:52

1 Answers1

1

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.

wrzasa
  • 1,113
  • 10
  • 20