Wednesday, July 08, 2009

JSR 223 JRuby Engine won't work on OSGi platform

I've been googling and seeking to find how I can run an application using JSR 223 JRuby Engine on OSGi platform. I could figure out some workarounds that fit JRuby Engine in OSGi world, but still get an error. When I ran a snippet on Apache Felix, I got "org.jruby.exceptions.RaiseException: library `java' could not be loaded: java.lang.ClassNotFoundException: org.jruby.javasupport.Java." This error happened during the initialization of Ruby instnace, at the line runtime.getLoadService().require("java"); in the initialize() method in JavaEmbedUtils. [Since I found this far before (not in this sample), I don't remember where the code is in my PC...] I'm not an OSGi savvy, I've lost the way of going forward. I thought that the recently filed issue http://jira.codehaus.org/browse/JRUBY-3792 might resolve this problem. The reporter said that the JRuby bundle is not perfect since it dynamically loads some libraries. This seems really close to my problem, so I rebuilt jruby-complete.jar after added DynamicImport-Package: * on the last line of jruby.bnd.templete and tried. The result was still the same, the error happened.

Although I could not make it, I'll write how I tried, hoping somebody will help me out.

  • pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hickory.example</groupId>
<artifactId>Hickory</artifactId>
<packaging>bundle</packaging>
<version>1.0-SNAPSHOT</version>
<name>Hickory</name>
<url>http://maven.apache.org</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Name>JRuby Engine example</Bundle-Name>
<Bundle-Description>Just outpus simple message from JRuby via JRuby Engine</Bundle-Description>
<Bundle-Vendor>JSR 223 for JRuby Project</Bundle-Vendor>
<Bundle-Version>1.0.0</Bundle-Version>
<Bundle-Activator>hickory.example.Activator</Bundle-Activator>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
<layout>default</layout>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.osgi.core</artifactId>
<version>1.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.livetribe</groupId>
<artifactId>livetribe-jsr223</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>com.sun.script.jruby</groupId>
<artifactId>jruby-engine</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

This pom.xml is for JDK 1.5, so I used livetribe version of javax.script package implementation. But, this archive is not OSGi bundle, so I had to use JDK 1.6 to start my bundle.
  • Snippet

package hickory.example;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {
public void start(BundleContext context) throws Exception {
System.out.println("Starting JRuby Engine!");
// JSR 223's discovery mechanism does not work on OSGi platform,
// so the engine factory instance should be directly instantiated.
//ScriptEngineManager manager = new ScriptEngineManager();
//manager.registerEngineName("jruby", new com.sun.script.jruby.JRubyScriptEngineFactory());
//ScriptEngine engine = manager.getEngineByName("jruby");
ScriptEngineFactory factory = (ScriptEngineFactory) new com.sun.script.jruby.JRubyScriptEngine();
System.out.println("Ready.");
ScriptEngine engine = factory.getScriptEngine();
engine.eval("puts \"JRuby wants to yell out, Hello World.\"");
}

public void stop(BundleContext context) {
System.out.println("JRuby Engine stopped.");
}
}

  • On Apache Felix

cd felix-1.8.0
java -jar bin/felix.jar

Welcome to Felix.
=================

-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
[ 13] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0)
-> start file:///Users/yoko/Tools/jruby-1.3.1/lib/jruby-complete.jar
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
[ 13] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0)
[ 28] [Active ] [ 1] JRuby 1.3.1 (1.3.1)
-> start file:///Users/yoko/.m2/repository/com/sun/script/jruby/jruby-engine/1.1.7/jruby-engine-1.1.7.jar
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
[ 13] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0)
[ 28] [Active ] [ 1] JRuby 1.3.1 (1.3.1)
[ 29] [Active ] [ 1] JRuby JSR223 Engine (1.0)
-> start file:///Users/yoko/NetBeansProjects/Hickory/target/Hickory-1.0-SNAPSHOT.jar
Starting JRuby Engine!
Warning: JRuby home "/28.0:1/META-INF/jruby.home" does not exist, using /var/folders/xY/xYuRYl0RHjy7p6SeA0nHVU+++TI/-Tmp-/
org.osgi.framework.BundleException: Activator start error in bundle hickory.example.Hickory [30].
at org.apache.felix.framework.Felix.startBundle(Felix.java:1506)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:779)
at org.apache.felix.shell.impl.StartCommandImpl.execute(StartCommandImpl.java:105)
at org.apache.felix.shell.impl.Activator$ShellServiceImpl.executeCommand(Activator.java:291)
at org.apache.felix.shell.tui.Activator$ShellTuiRunnable.run(Activator.java:177)
at java.lang.Thread.run(Thread.java:637)
Caused by: org.jruby.exceptions.RaiseException: library `java' could not be loaded: java.lang.ClassNotFoundException: org.jruby.javasupport.Java
at (unknown).initialize(:1)
at (unknown).(unknown)(:1)
org.jruby.exceptions.RaiseException: library `java' could not be loaded: java.lang.ClassNotFoundException: org.jruby.javasupport.Java
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
[ 13] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0)
[ 28] [Active ] [ 1] JRuby 1.3.1 (1.3.1)
[ 29] [Active ] [ 1] JRuby JSR223 Engine (1.0)
[ 30] [Resolved ] [ 1] JRuby Engine example (1.0.0)

3 comments:

Unknown said...

Well, no idea how you've got that specific exception - if Ruby.class is found in the classpath, how come Java.class isn't.. These are in the same jar for me. Are they there in yours?

Perhaps you can try with another OSGI container for now.

I used Eclipse Equinox, where my initial classpath problem was that a script wasn't found if it was in another bundle than jruby. The solution there was to use Equinox extensions(buddy policies &co);

If you want, I could paste in Manifest that I found working with jruby-complete on Eclipse.

yokolet said...

I think the exception comes from the failure of JRuby specific classloader. This JRuby's classloader is instantiated by Ruby runtime and loads other classes dynamically in runtime. I guess the parent classloaders of JRuby specific classloader and OSGi platform never meet with.

I'll try Equinox, but want to make this work on Apache Felix since GlassFish uses Apache Felix for its OSGi platform.

I'm abolutely interested in your Manifest, so please paste it in somewhere.

Unknown said...

Try adding

Import-Bundle: org.jruby.jruby

(or whatever the jruby-complete's Bundle-SymbolicName is) to the manifest for the bundle with your script.