Search code examples
javajruby

Calling Java code snippet from JRuby?


How can I call a Java code snippet from JRuby code? My code snippet is really short, actually it's just a set of a few Java statements.


Solution

  • Explained here on how to call existing Java code from JRuby. The most basic usage:

    require 'java'
    java.lang.System.out.println("Hello, world!")
    

    As a bit more complex example, if you want to import your arbitrary package (say, 'foo.bar.baz') from a JAR, you can do this:

    require 'java'
    require 'foobarbaz.jar'
    def foo
      Java::Foo
    end
    shiny_thingy = foo.bar.baz.Thingy.new("Shiny")
    shiny_thingy.shine()
    

    If you want to evaluate a string as if it was Java, you would need to compile it first; you can use the techniques in this question, but Java generally frowns on autogenerated code, and it is not trivial to do it. Or you can translate it into JRuby, calling Java classes as described above, and skip the compilation issue.

    We might be able to help better if we knew what your snippet consisted of.

    EDIT: Here is the adaptation of the linked code that will instantiate an arbitrary class. Be aware that it will create .class files, which is AFAIK inevitable when a compilation step is involved. The code assumes a subdirectory named tmp exists; adapt to your use case.

    shiny_source = <<-EOF
      package foo.bar.baz;
      public class Shiny {
        public Shiny() {
          System.out.println("I'm shiny!");
        }
      }
    EOF
    
    require 'java'
    java_import javax.tools.SimpleJavaFileObject
    java_import java.net.URI
    
    class JavaSourceFromString < SimpleJavaFileObject
      def initialize(name, code)
        uri = "string:///" + name.gsub('.', '/') + Kind::SOURCE.extension
        super URI.create(uri), Kind::SOURCE
        @code = code
      end
    
      def getCharContent(ignore_encoding_errors)
        @code
      end
    end
    
    java_import javax.tools.ToolProvider
    java_import java.io.StringWriter
    java_import java.net.URL
    java_import java.net.URLClassLoader
    
    compilation_path = java.nio.file.Paths.get('tmp').to_absolute_path.to_s
    jc = ToolProvider.get_system_java_compiler
    raise "Compiler unavailable" unless jc
    
    jsfs = JavaSourceFromString.new('foo.bar.baz.Shiny', shiny_source)
    file_objects = [jsfs]
    ccl = java.lang.Thread.current_thread.get_context_class_loader
    classpath = ccl.getURLs.to_a.join(java.io.File::pathSeparator)
    options = ['-d', compilation_path, '-classpath', classpath]
    output = StringWriter.new
    success = jc.get_task(output, nil, nil, options, nil, file_objects).call
    raise output unless success
    
    url = URL.new("file:" + compilation_path + "/")
    ucl = URLClassLoader.new_instance([url].to_java(URL))
    shiny_class = ucl.load_class('foo.bar.baz.Shiny')
    shiny_class.new_instance