100% AOT compiled jruby with spring


#1

Hi,

I am trying to create a 100 % AOT jruby app integrated with
springframework.
I am using jruby-1.2.0.

For performance reasons our goal is to have the ruby app with all the
dependencies compiled into jar files with no access whatsoever to rb
files or jruby home.

Is this possible?

I tried it for a webapp to show the result of a simple query using
activerecord for the DB query. I compiled my spring controller in ruby
and the below ruby dependencies using jrubyc:

activerecord-2.3.2
activerecord-jdbc-adapter-0.9
activesupport-2.3.2
rubygems
lib/ruby/1.8

I am running into trouble when I use RubyGems to include
‘activerecord-jdbc-adapter’ which seems to be a problem in general with
AOT compilation of scripts using gems. The same example works fine if I
add a -Djruby.home=<ruby_home> to my tomcat startup script.
Otherwise I get the below error:

Could not find RubyGem activerecord-jdbc-adapter (>= 0)

Below are the detailed steps I followed. The spring configurations are
just integration details and the below can apply to a java app also:

File RubyTestController.rb (A Ruby Controller extending Spring’s
Controller):

require ‘java’

require ‘rubygems’
gem ‘activerecord-jdbc-adapter’
require ‘jdbc_adapter’
require ‘active_record’

class RubyTestController <
org.springframework.web.servlet.mvc.AbstractController
def handleRequestInternal(request, response)
op = response.getOutputStream();

ActiveRecord::Base.establish_connection(
  :adapter => 'jdbc',
  :jndi => "java:comp/env/jdbc/ctb",
  :driver => "oracle.jdbc.driver.OracleDriver"
);
con = ActiveRecord::Base.connection();
results = con.execute("select hotel_name from hotel_geography_vw 

where hotel_id = 119526");
results.each do |row|
column = row[‘hotel_name’]
op.println(column);
end
ActiveRecord::Base.remove_connection();
return nil;
end
end

File RubyBeanFactory.java (Bean Factory creating above for Spring):

import javax.script.*;

public class RubyBeanFactory {
public static Object createRubyBean(String className) throws
Exception {
/**
* Engine manager and engine will be instantiate only once at the
class level. What I am doing below, is just for testing.
*/
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine rubyEngine = m.getEngineByName(“jruby”);

  Object data = null;
  /**
   * We can either call a ruby file, or give an ruby expression for 

evaluation.
*/
data = rubyEngine.eval(“require '” + className + “’\n” + className

  • “.new()”);
    return data;
    }
    }

Deployment Steps:

  1. Copy jruby.jar jruby-engine.jar tomcat’s lib directory

  2. Add spring and related jar’s to console classpath:

export
CLASSPATH=/c/dev-depot/common/lib/commons-logging.jar:/c/installs/apache-tomcat-6.0.14/lib/servlet-api.jar:/c/installs/jruby-1.2.0/lib/jruby.jar:/c/dev-depot/common/lib/spring-webmvc.jar:/c/dev-depot/common/lib/spring-web.jar:/c/dev-depot/common/lib/spring-context.jar:/c/dev-depot/common/lib/spring-beans.jar:/c/dev-depot/common/lib/spring-core.jar:/c/installs/apache-tomcat-6.0.14/lib/jruby-engine.jar:.

  1. Generate compiled_rbs.jar and copy to tomact’s lib dir:

jrubyc RubyTestController.rb
javac RubyBeanFactory.java
jar cf compiled_rbs.jar RubyTestController.class RubyBeanFactory.class

  1. Spring app-servlet.xml config (Also add a URL entry for ruby_test in
    web.xml):






    testController


  2. Compile required ruby dependencies and add them to ruby_gems.jar:

cd jruby-1.2.0/lib/ruby/1.8
jrubyc .
jar cf …/ruby_gems.jar find . -name '*.class'

cd jruby-1.2.0/lib/ruby/site_ruby/1.8
jrubyc rubygems.rb rubygems
jar uf …/…/ruby_gems.jar rubygems.class find rubygems -name '*.class'

jruby -S gem install activerecord-jdbc-adapter
cd
jruby-1.2.0/lib/ruby/gems/1.8/gems/activerecord-jdbc-adapter-0.9.1/lib
jrubyc .
jar uf …/…/…/…/…/ruby_gems.jar find . -name '*.class'

jruby -S gem install activerecord

cd jruby-1.2.0/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib
jrubyc .
jar uf …/…/…/…/…/ruby_gems.jar find . -name '*.class'

cd jruby-1.2.0/lib/ruby/gems/1.8/gems/activesupport-2.3.2/lib
jrubyc .
jar uf …/…/…/…/…/ruby_gems.jar find . -name '*.class'

  1. Copy ruby_gems.jar to tomcat lib directory and start tomcat.

I get the below error:

2009-03-18 14:31:39,348 ERROR
[org.springframework.web.servlet.DispatcherServlet] -
org.springframework.beans.factory.BeanCreationException: Error creating
bean with name ‘testController’ defined in ServletContext resource
[/WEB-INF/hotel-servlet.xml]: Instantiation of bean failed; nested
exception is
org.springframework.beans.factory.BeanDefinitionStoreException: Factory
method [public static java.lang.Object
RubyBeanFactory.createRubyBean(java.lang.String) throws
java.lang.Exception] threw exception; nested exception is
javax.script.ScriptException: org.jruby.exceptions.RaiseException: Could
not find RubyGem activerecord-jdbc-adapter (>= 0)

at
org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:833)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:747)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:412)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:383)
at java.security.AccessController.doPrivileged(Native Method)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:353)
at
org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
at
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:169)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:400)
at
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:736)
at
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:369)
at
org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:332)
at
org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:266)
at
org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:236)
at
org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:126)
at javax.servlet.GenericServlet.init(GenericServlet.java:212)
at
org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1161)
at
org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:981)
at
org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4045)
at
org.apache.catalina.core.StandardContext.start(StandardContext.java:4351)
at
org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at
org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at
org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at
org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:825)
at
org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:714)
at
org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:490)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1138)
at
org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at
org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at
org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at
org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at
org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at
org.apache.catalina.core.StandardService.start(StandardService.java:516)
at
org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:566)
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.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Caused by:
org.springframework.beans.factory.BeanDefinitionStoreException: Factory
method [public static java.lang.Object
RubyBeanFactory.createRubyBean(java.lang.String) throws
java.lang.Exception] threw exception; nested exception is
javax.script.ScriptException: org.jruby.exceptions.RaiseException: Could
not find RubyGem activerecord-jdbc-adapter (>= 0)

at
org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:127)
at
org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:404)
… 44 more
Caused by: javax.script.ScriptException:
org.jruby.exceptions.RaiseException: Could not find RubyGem
activerecord-jdbc-adapter (>= 0)

at
com.sun.script.jruby.JRubyScriptEngine.evalNode(JRubyScriptEngine.java:498)
at
com.sun.script.jruby.JRubyScriptEngine.eval(JRubyScriptEngine.java:182)
at
javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)
at RubyBeanFactory.createRubyBean(RubyBeanFactory.java:15)
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.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:115)
… 45 more
Caused by: org.jruby.exceptions.RaiseException: Could not find RubyGem
activerecord-jdbc-adapter (>= 0)

at Kernel.raise(./rubygems.rb:636)
at #Class:01x1ceae51.report_activate_error(./rubygems.rb:141)
at #Class:01x1ceae51.activate(./rubygems.rb:49)
at Kernel.gem(RubyTestController.rb:4)
at Kernel.require(:1)
at (unknown).(unknown)(:1)

Tbanks,
Suresh


Be Yourself @ mail.com!
Choose From 200+ Email Addresses
Get a Free Account at www.mail.com


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

#2

I think there’s a few others here who have precompiled all of Rails and
gotten applications to work, but it’s a little tricky. Rails tends to
use a lot of filesystem APIs for loading things, so in some cases I
believe you have to patch around that.

Anyone else doing precompilation of Rails core for an application?

M Suresh wrote:

results = con.execute("select hotel_name from hotel_geography_vw where hotel_id = 119526");

jar cf compiled_rbs.jar RubyTestController.class RubyBeanFactory.class

jar cf …/ruby_gems.jar find . -name '*.class'
jruby -S gem install activerecord

at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:236)
at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:714)
at org.apache.catalina.startup.Catalina.start(Catalina.java:566)
… 44 more
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:115)
Tbanks,
Suresh


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email