I'm not in the right place to give a detailed answer today, but we have JRuby working well under OSGi on iucnredlist.org, with the interpreter exposed as a service ... I will drill into your example more tomorrow and see if our approaches are applicable. Might be.
On Jan 1, 2010, at 9:56 PM, Matt Stine <[email protected]> wrote: > Hi, all. Charles Nutter requested via Twitter that I post up the > issues I'm having getting JRuby to play nice in an OSGi environment. > > You can find all of the code in question at http://github.com/mstine/pgovm > . > > Basically, my goal is to demonstrate the ability to implement OSGi > services in alternative JVM languages without exposing the chosen > implementation language to consumers of those services. I do this by > providing a public API as a Java interface (see the pgo-vm-api > module). This API provides all of the data types necessary to consume > the service. I then implement the API in other bundles, and expose > those implementations as services, witht the service interface being > the Java interface from the public API. I've successfully done this so > far in: > > - Java > - Groovy > - Scala > > This week I set out to do the same in JRuby. First off, kudos to the > fact that the JRuby JARS available in the Codehaus Maven repo are > proper OSGi bundles. As it turns out, its actually quite easy to > execute some arbitrary Ruby code from within an OSGi bundle. All one > needs to do is make sure that the JRuby 1.4.0 bundle is properly > provisioned into the container and then use the new embeded script > runner to fire it off. The first problem occurs when trying to > implement the Java interface (or reference any Java code outside of > the JRuby bundle for that matter). The classloader currently in scope > cannot see the interface. The only workaround I've found so far that > seems to get anywhere is to add a directive to the JRuby jar manifest: > > DynamicImport-Package: * > > This isn't entirely desirable, as in basically cracks open the entire > world for JRuby to see. Any package that isn't explicitly imported by > JRuby can now potentially be found by scanning all available bundles, > and if found, access them. At this point, our first problem is solved. > If I use pax-provision (part of Pax Construct, see > http://wiki.ops4j.org/display/paxconstruct/Pax+Construct) to start up > the OSGi container and provision JRuby, the API bundle, and the JRuby > service implementation bundle, all seems to go well, as the service > bundle is started and the service registered: > > osgi> ss > > Framework is launched. > > id State Bundle > 0 ACTIVE org.eclipse.osgi_3.5.1.R35x_v20090827 > 1 ACTIVE > com.mattstine.polyglotosgi.vendingmachine.pgo-vm-jruby- > impl_1.0.0.SNAPSHOT > 2 ACTIVE > com.mattstine.polyglotosgi.vendingmachine.pgo-vm-api_1.0.0.SNAPSHOT > 3 ACTIVE org.jruby.jruby_1.4.0 > > osgi> bundle 1 > com.mattstine.polyglotosgi.vendingmachine.pgo-vm-jruby- > impl_1.0.0.SNAPSHOT [1] > Id=1, Status=ACTIVE Data > Root=/Users/mstine/Projects/polyglot-osgi-talk/code/pgovm/pgo-vm- > jruby-impl/runner/equinox/org.eclipse.osgi/bundles/1/data > Registered Services > {com.mattstine.polyglotosgi.vendingmachine.api.VendingMachine}= > {service.id=26} > > You can see here that bundle 1 is the JRuby implementation bundle, and > that it exposes a service via the API Java interface. > > On to the next issue....in-container testing. I have a set of tests > for this API that all of the various implementations should pass. > These are implemented via a JUnit 4 test case using Pax Exam, which is > a support framework for testing OSGi bundles (see > http://wiki.ops4j.org/display/paxexam/Pax+Exam). The pgo-vm-tests > module in the GitHub project contains the test cases as well as the > proper pom.xml for leveraging Pax Exam. Basically, Pax Exam starts up > an OSGi container of choice, provisions your requested bundles, and > then runs the unit tests against them. I've attempted this against the > three major containers (Equinox, Felix, and Knopflerfish), and I get > different results, both seeming related to classloading. On Equinox, > here you can see an excerpt of the test bootstrapping: > > [ PaxRunnerTestContainer] - Test container (Pax Runner 0.17.2) > started in 25545 millis > STARTING com.mattstine.polyglotosgi.vendingmachine.jruby > REGISTER > com.mattstine.polyglotosgi.vendingmachine.jruby.internal.VendingMachineJRubyImpl > [ RemoteBundleContextClient] - Remote bundle context found after > 5887 millis > VM IMPL = org.jruby.gen.interfaceimpl1814023...@19ed13da > [ PaxRunnerTestContainer] - Shutting down the test container > (Pax Runner) > > STOPPING > com.mattstine.polyglotosgi.vendingmachine.jruby.internal.VendingMachineJRubyImpl > > [ DefaultJavaRunner] - Platform has been shutdown. > > What's important to note is that the test class is actually able to > get an instance of the service, as evidenced by the println statement > on the 5th line of output above. That println occurs in the setup > method (annotated w/ @Before) in the test case. However, when we > actually try to execute the first test method, things blow up, as > below: > > --- > --- > --- > ---------------------------------------------------------------------- > Test set: > com.mattstine.polyglotosgi.vendingmachine.tests.jruby.VendingMachineJRubyTests > --- > --- > --- > ---------------------------------------------------------------------- > Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 32.766 > sec <<< FAILURE! > testEmptyInventory > [equinox] > (com.mattstine.polyglotosgi.vendingmachine.tests.jruby.VendingMachineJRubyTests) > Time elapsed: 32.706 sec <<< ERROR! > java.lang.NoClassDefFoundError: org/jruby/java/MiniJava > at org.jruby.gen.InterfaceImpl1814023411.showInventory(org/ > jruby/gen/InterfaceImpl1814023411.gen:5) > at > com.mattstine.polyglotosgi.vendingmachine.tests.jruby.VendingMachineJRubyTests.testEmptyInventory( > VendingMachineJRubyTests.java:37) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at sun.reflect.NativeMethodAccessorImpl.invoke > (NativeMethodAccessorImpl.java:39) > at sun.reflect.DelegatingMethodAccessorImpl.invoke > (DelegatingMethodAccessorImpl.java:25) > at java.lang.reflect.Method.invoke(Method.java:597) > at > org.ops4j.pax.exam.junit.extender.impl.internal.CallableTestMethodImpl.injectContextAndInvoke( > CallableTestMethodImpl.java:128) > at > org.ops4j.pax.exam.junit.extender.impl.internal.CallableTestMethodImpl.call( > CallableTestMethodImpl.java:96) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at sun.reflect.NativeMethodAccessorImpl.invoke > (NativeMethodAccessorImpl.java:39) > at sun.reflect.DelegatingMethodAccessorImpl.invoke > (DelegatingMethodAccessorImpl.java:25) > at java.lang.reflect.Method.invoke(Method.java:597) > at > org.ops4j.pax.exam.rbc.internal.RemoteBundleContextImpl.remoteCall > (RemoteBundleContextImpl.java:78) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at sun.reflect.NativeMethodAccessorImpl.invoke > (NativeMethodAccessorImpl.java:39) > at sun.reflect.DelegatingMethodAccessorImpl.invoke > (DelegatingMethodAccessorImpl.java:25) > at java.lang.reflect.Method.invoke(Method.java:597) > at sun.rmi.server.UnicastServerRef.dispatch > (UnicastServerRef.java:305) > at sun.rmi.transport.Transport$1.run(Transport.java:159) > at java.security.AccessController.doPrivileged(Native Method) > at sun.rmi.transport.Transport.serviceCall(Transport.java:155) > at sun.rmi.transport.tcp.TCPTransport.handleMessages > (TCPTransport.java:535) > at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0 > (TCPTransport.java:790) > at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run > (TCPTransport.java:649) > at java.util.concurrent.ThreadPoolExecutor$Worker.runTask > (ThreadPoolExecutor.java:886) > at java.util.concurrent.ThreadPoolExecutor$Worker.run > (ThreadPoolExecutor.java:908) > at java.lang.Thread.run(Thread.java:637) > Caused by: java.lang.ClassNotFoundException: org.jruby.java.MiniJava > at java.net.URLClassLoader$1.run(URLClassLoader.java:200) > at java.security.AccessController.doPrivileged(Native Method) > at java.net.URLClassLoader.findClass(URLClassLoader.java:188) > at java.lang.ClassLoader.loadClass(ClassLoader.java:315) > at java.lang.ClassLoader.loadClass(ClassLoader.java:250) > at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:398) > ... 27 more > > On Felix (and very similar on Knopflerfish), we actually don't even > get an instance of the service, as it blows up at startup: > > STARTING com.mattstine.polyglotosgi.vendingmachine.jruby > REGISTER > com.mattstine.polyglotosgi.vendingmachine.jruby.internal.VendingMachineJRubyImpl > ERROR: Error starting > file:/var/folders/4r/4r4zqex3Fs8-koSqNtfdvU+++TM/-Tmp-/ > paxexam_runner_mstine/bundles/ > com.mattstine.polyglotosgi.vendingmachine.pgo-vm-jruby- > impl_1.0.0.SNAPSHOT.jar > (org.osgi.framework.BundleException: Activator start error in bundle > com.mattstine.polyglotosgi.vendingmachine.pgo-vm-jruby-impl [7].) > :1: library `enumerator' could not be loaded: > java.lang.ClassNotFoundException: > org.jruby.libraries.EnumeratorLibrary (LoadError) > ...internal jruby stack elided... > from (unknown).(unknown)(:1) > [ RemoteBundleContextClient] - Remote bundle context found after > 3430 millis > VM IMPL = null > [ PaxRunnerTestContainer] - Shutting down the test container > (Pax Runner) > > > [ DefaultJavaRunner] - Platform has been shutdown. > > In both cases, it seems as if the classloader cannot see portions of > JRuby. What's frustrating is that even if I bring in all of the JRuby > packages via a Require-Bundle directive, I still get this behavior in > all three containers. > > Any ideas? Any help would be greatly appreciated. > > Thanks! > > > > > Matt Stine > Java User Group Leader > > Memphis/Mid-South Java User Group > Mail Stop 754 > 262 Danny Thomas Place > Memphis, TN 38105 USA > Phone: 901-214-JAVA > Email: [email protected] > http://www.linkedin.com/in/mattstine > http://www.mattstine.com > > > Want a signature like this? > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email
