Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

become_java! does not support concrete class extension #2359

Closed
headius opened this issue Dec 29, 2014 · 5 comments
Closed

become_java! does not support concrete class extension #2359

headius opened this issue Dec 29, 2014 · 5 comments

Comments

@headius
Copy link
Member

headius commented Dec 29, 2014

This is a long standing issue that will likely require a major overhaul of how we do class extension. Sadly, many things require that same overhaul, and the code involved is very rough.

Original issue: http://jira.codehaus.org/browse/JRUBY-6105

Example code that fails to work:

require 'java'
require 'jruby/core_ext'

# brew install hadoop
HADOOP_ROOT = "/usr/local/Cellar/hadoop/0.21.0/libexec/"
Dir.glob( File.join( HADOOP_ROOT, "**/*.jar" ) ).each do |jar|
  require jar
end

# http://hadoop.apache.org/mapreduce/docs/r0.21.0/api/org/apache/hadoop/mapreduce/Mapper.html
# org.apache.hadoop.mapreduce Class Mapper<KEYIN,VALUEIN,KEYOUT,VALUEOUT>
Mapper = org.apache.hadoop.mapreduce.Mapper

class TestA < Mapper
end

puts "Ruby TestA class : #{TestA.name}"

puts "Test A Ancestors: #{TestA.ancestors.join(", ")}"

java_class = TestA.become_java!

if java_class.nil? then
  puts "become_java! returned nil"
  puts "TestA name = #{TestA.name}"
else
  puts java_class.interfaces.each { |i| puts i }
end

__END__
% ruby ./j.rb
Ruby TestA class : TestA
Test A Ancestors: TestA, Java::OrgApacheHadoopMapreduce::Mapper, Java::JavaLang::Object, ConcreteJavaProxy, JavaProxy, JavaProxyMethods, Object, Kernel
become_java! returned nil
TestA name = TestA
@paneq
Copy link

paneq commented Sep 30, 2016

Looks like this makes using JRuby with Akka impossible:

require 'jruby'
require 'jruby/core_ext'
require './akka-2.4.11/lib/scala-library-2.11.8.jar'
require './akka-2.4.11/lib/akka/akka-actor_2.11-2.4.11.jar'
require './config-1.3.1.jar'

java_import 'akka.actor.ActorRef'
java_import 'akka.actor.UntypedActor'
java_import 'akka.actor.ActorSystem'
java_import 'akka.actor.Props'
java_import 'akka.actor.Inbox'
java_import 'scala.concurrent.duration.Duration'
java_import 'scala.concurrent.duration.FiniteDuration'

java_import 'java.io.Serializable'
java_import 'java.util.concurrent.TimeUnit'
java_import 'java.util.concurrent.TimeoutException'

class HelloAkkaJava < UntypedActor
  def onReceive(message)
    puts message.inspect
  end
end
cls = HelloAkkaJava.become_java!

system = ActorSystem.create('JRubyOnAkka')
greeter = system.actorOf(Props.create(cls), "greeter")

as you need to pass Java class to Props.create

@mooreniemi
Copy link

@paneq can you use dependency injection? i was able to get around this problem that way.

@paneq
Copy link

paneq commented Oct 2, 2016

@mooreniemi Thanks for the suggestion, sounds very good and interesting. Could you elaborate though what you have in mind exactly? Maybe a small contrived example?

@mooreniemi
Copy link

@paneq The basic idea is that rather than inheriting directly on the Ruby object directly, you just delegate forward relevant calls to an embedded Ruby object.

My requirement was to have pure Ruby endpoints that all inherited from a Java framework. When I needed to inherit from BaseEndpoint I instead injected in the Ruby object I needed to call and generated an EndpointWrapper class for every injected Ruby class:

public class EndpointWrapper extends BaseEndpoint {
  // this is the JRuby endpoint we'll inject in
  protected static IRubyObject endpoint;

  public EndpointWrapper() {
  }

  // can't inject on construction, so need this setter
  public static void setEndpoint(IRubyObject rubyEndpoint) {
    endpoint = rubyEndpoint;
  }

  @Override
  protected Map execute(CheetahQuery query) throws Exception {
    ThreadContext context = endpoint.getRuntime().getCurrentContext();
    // we're forwarding the execute method to our injected dep
    IRubyObject called = endpoint.callMethod(
        context,
        "execute",
        JavaObject.wrap(
          endpoint.getRuntime(),
          query
          )
        );

    return called.convertToHash();
  }
}

So things are basically like this:

    +Cheetah----------------------------------+
    | (workerbee inserts all the below)       |
    |                                         |
    | @Endpoint("/route1")                    |
    | +Route1EndpointWrapper----------------+ |
    | |  execute(query) {                   | |
    | |    endpoint.execute(query)          | |
    | |  }                                  | |
    | |                                     | |
    | |  IRubyObject endpoint               | |
    | |  Route1Endpoint------------------+  | |
    | |  + def execute(query)            |  | |
    | |  |   super                       |  | |
    | |  |   your_custom_logic           |  | |
    | |  |   {"key": "values"}           |  | |
    | |  | end                           |  | |
    | |  |                               |  | |
    | |  | def your_custom_logic         |  | |
    | |  |                               |  | |
    | |  | end                           |  | |
    | |  +-------------------------------+  | |
    | +-------------------------------------+ |
    +-----------------------------------------+

@Overbryd
Copy link

I just bumped into this, I would love to be able to provide Ruby-classes to a Java framework (Apache NiFi in this case). But in order for them to work, I need them to be able to subclass from a base class of the Java framework.

The workaround I have now, is to have a generated Java-written class, that calls into the ruby class. Quite similar to what @mooreniemi has done.

@headius headius added this to the JRuby 9.3.4.0 milestone Mar 31, 2022
@headius headius closed this as completed Mar 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants