JRuby-Rack internal questions to integrate Quartz in JRoR

Hello list,

I wrote a rails app and deployed it (with warbler and JRuby-Rack) in
tomcat.
Now, I need some sheduling functionality for health check, reporting,

Because I have a java background and I’m familiar with, I decided to use
Quartz (http://www.opensymphony.com/quartz/).

My first (naive) approache was, to implement the (java) interface
org.quartz.Job in jruby and provide a simple logging Job:
RAILS_ROOT/lib/logging_job.rb:

require ‘java’
require ‘lib/commons-logging-1.1.jar’
require ‘lib/commons-collections-3.2.jar’
require ‘lib/commons-pool-1.3.jar’
require ‘lib/quartz-all-1.6.4.jar’

include_class ‘org.quartz.Job’
include_class ‘org.quartz.JobExecutionContext’

class LoggingJob
include org.quartz.Job

def execute context
RAILS_DEFAULT_LOGGER.info “LoggingJob scheduled at #{Time.now}”
end
end

The next thing was to shedule the job. This is finished with some lines
of
code:
RAILS_ROOT/config/initializers/quartz_sheduler.rb

require ‘java’
require ‘lib/commons-logging-1.1.jar’
require ‘lib/commons-collections-3.2.jar’
require ‘lib/commons-pool-1.3.jar’
require ‘lib/quartz-all-1.6.4.jar’
require ‘logging_job’

include_class ‘org.quartz.impl.StdSchedulerFactory’
include_class ‘org.quartz.JobDetail’
include_class ‘org.quartz.CronTrigger’

RAILS_DEFAULT_LOGGER.info ‘intializing Quartz Scheduler…’

scheduler = org.quartz.impl.StdSchedulerFactory.getDefaultScheduler
scheduler.startDelayed 30
scheduler.start

RAILS_DEFAULT_LOGGER.info ‘intializing LoggingJob…’
job = org.quartz.JobDetail.new(‘LoggingJob’, ‘railsJob’,
LoggingJob.java_class) # failed also with LoggingJob.new.java_class
trigger = org.quartz.CronTrigger.new(‘LoggingCronTrigger’, ‘railsJob’,
'0 *

      • ?') # runs every minute
        scheduler.scheduleJob(job, trigger)

RAILS_DEFAULT_LOGGER.info ‘Finished initializing quartz scheduler and
jobs’

But the problem is, that quartz can’t instantiate the (j)ruby class
LoggingJob from java… :frowning:
I hope it is possible in the future. :slight_smile:
But if i wrong, please point it out.

My next attempt was to write in java a ServletContextListener which has
the
same functionality as the quartz_sheduler.rb and a RailsJob, which
implements the org.quartz.Job interface and simply load a ruby script
and
execute it. I found an article and a plugin from Jens Krämer
http://www.jkraemer.net/2008/1/12/job-scheduling-with-jruby-and-rails.
The
code is now hosted here
http://github.com/macarthy/mirror-quartz-jruby-plugin/tree/master.
But the code is incompatible with the JRuby-Rack trunk. So, I deceided
to
use this code as a starting point and update it. To lookup the ruby
runtime,
I do the following:

RackApplicationFactory factory = (RackApplicationFactory)
application.getAttribute(RackServletContextListener.FACTORY_KEY);
RackApplication rackApplication = null;
try {
rackApplication = factory.getApplication();
Ruby runtime = rackApplication.getRuntime();
} finally {
if (rackApplication != null) {
factory.finishedWithApplication(rackApplication);
}
}

But the code crashes and I have no idee why… :frowning:
Then, I wrote a simple JSP to find the reason:
WEB-INF/index2.jsp

<%@ page language=“java” contentType=“text/html; charset=ISO-8859-1”
pageEncoding=“ISO-8859-1”%>
<%@ page import=“org.jruby., org.jruby.rack.”%>

Insert title here <% RackApplicationFactory factory = (RackApplicationFactory) application.getAttribute(RackServletContextListener.FACTORY_KEY); RackApplication rackApplication = null; try { rackApplication = factory.getApplication(); Ruby runtime = rackApplication.getRuntime(); } finally { if (rackApplication != null) { factory.finishedWithApplication(rackApplication); } } %>

This gives me the following stack trace (JRuby-Rack 0.9.2 and JRuby
1.1.4):

org.jruby.rack.RackInitializationException: exit
from
/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:38:in
run' from file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/jruby/rack/rails_boot.rb:20:in run’
from
/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:11:in
boot!' from /Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:109 from /Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:11:in require’
from
/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/environment.rb:11
from
/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/environment.rb:29:in
load' from file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/jruby/rack/rails.rb:29:in load_environment’
from
file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/jruby/rack/rails.rb:152:in
new' from <script>:3 from file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22:in instance_eval’
from
file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22:in
`initialize’
from :3

org.jruby.rack.DefaultRackApplicationFactory$4.init(DefaultRackApplicationFactory.java:154)
org.jruby.rack.DefaultRackApplicationFactory.getApplication(DefaultRackApplicationFactory.java:53)
org.jruby.rack.PoolingRackApplicationFactory.getApplication(PoolingRackApplicationFactory.java:92)
org.apache.jsp.index2_jsp._jspService(index2_jsp.java:68)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:374)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.jruby.rack.RackFilter.doFilter(RackFilter.java:47)

root cause

org.jruby.exceptions.RaiseException
#Class:01xde81ff.load_rubygems(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:52)
Rails::GemBoot.load_initializer(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:38)
Rails::Boot.run(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/jruby/rack/rails_boot.rb:20)
Rails::BootHook.run(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:11)
#Class:01xe6f118.boot!(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:109)
(unknown).(unknown)(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:11)
Kernel.require(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/environment.rb:11)
(unknown).(unknown)(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/environment.rb:29)
Kernel.load(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/jruby/rack/rails.rb:29)
JRuby::Rack::RailsServletHelper.load_environment(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/jruby/rack/rails.rb:152)
#Class:01x22d561.new(:3)
(unknown).(unknown)(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22)
Kernel.instance_eval(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22)
Kernel.instance_eval(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22)
Rack::Builder.initialize(:3)
(unknown).(unknown)(:1)

Any idee why my code blows up?

Thanks for every help,
Christian

On Sun, Dec 7, 2008 at 12:57 PM, Christian
Müller[email protected] wrote:

Hello list,

I wrote a rails app and deployed it (with warbler and JRuby-Rack) in tomcat.
Now, I need some sheduling functionality for health check, reporting, …
Because I have a java background and I’m familiar with, I decided to use
Quartz (http://www.opensymphony.com/quartz/).

Cool. Let me know how this progresses, maybe I can integrate it into
either JRuby-Rack or an offshoot project. Or start your own if you
like.

(unknown).(unknown)(/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:11)

Kernel.instance_eval(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22)
Rack::Builder.initialize(:3)
(unknown).(unknown)(:1)

Any idee why my code blows up?

If you look in config/boot.rb, there are a number of ways that Rails
exits, either if you don’t have the Rails gem properly installed, or
if you don’t have a recent enough version of Rubygems. Are either of
these the problem?

/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Cool. Let me know how this progresses, maybe I can integrate it into
either JRuby-Rack or an offshoot project. Or start your own if you
like.

I will share my knowledge with the community and create a gem/plugin.
Maybe
we find an other integration solution (JRuby-Rack, …). I will keep you
up-to-date…

If you look in config/boot.rb, there are a number of ways that Rails

exits, either if you don’t have the Rails gem properly installed, or
if you don’t have a recent enough version of Rubygems. Are either of
these the problem?

My favorite combinations are jruby-1.1.5 + jruby-rack-0.9.2 +
rails-2.2.2 +
rubyGems-1.3.1 or jruby-1.1.6RC1 + jruby-rack-0.9.3 + rails-2.2.2 +
rubyGems-1.3.1. It is ok?
At this moment, what are your recommendation for the development?

Christian

On Mon, Dec 8, 2008 at 3:51 AM, Christian
Müller[email protected] wrote:

If you look in config/boot.rb, there are a number of ways that Rails
exits, either if you don’t have the Rails gem properly installed, or
if you don’t have a recent enough version of Rubygems. Are either of
these the problem?

My favorite combinations are jruby-1.1.5 + jruby-rack-0.9.2 + rails-2.2.2 +
rubyGems-1.3.1 or jruby-1.1.6RC1 + jruby-rack-0.9.3 + rails-2.2.2 +
rubyGems-1.3.1. It is ok?
At this moment, what are your recommendation for the development?

I have personally seen some problems with JRuby 1.1.5 and Rubygems
1.3.1, but I can’t tell if that’s what is causing your problem. Is
there any difference in the error you see when using JRuby 1.1.5 vs.
JRuby 1.1.6RC1? I’ll be recommending JRuby 1.1.6 as soon as it’s
released.

/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Hi Matt,

yes I have. But first, I looking for a more ruby like solution and when
this
failed, I found Jens plugin and the “rebuild” or update of this plugin
failes… :frowning:

Christian

We ran into that problem too. I forked the git repo I had linked to, and
added java source, our changes, and a quick change to use jruby-rack
instead of goldspike. It builds now, but I haven’t tested it.

Hope that helps,
Matt

Christian Müller wrote:

    My first (naive) approache was, to implement the (java)
    include_class 'org.quartz.JobExecutionContext'
    some lines of code:
    include_class 'org.quartz.JobDetail'
    LoggingJob.java_class) # failed also with LoggingJob.new.java_class
    I hope it is possible in the future. :-)
    http://github.com/macarthy/mirror-quartz-jruby-plugin/tree/master.
    } finally {
    charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
           <%    RackApplicationFactory factory =
               }
    /Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/boot.rb:38:in
           from
    `load_environment'
    file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22:in
           org.apache.jsp.index2_jsp._jspService(index2_jsp.java:68)
     org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)

         
         

  http://xircles.codehaus.org/manage_email

To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Out of curiosity, have you looked at the quartz_rails plugin? It’s still
built for jruby 1.0.1, so it’d need to be rebuilt, but might be a good
starting point for you.

http://www.jkraemer.net/2008/1/12/job-scheduling-with-jruby-and-rails
http://github.com/macarthy/mirror-quartz-jruby-plugin/tree/master


Matt

Christian Müller wrote:

include org.quartz.Job

‘0 * * * * ?’) # runs every minute

}

Ruby runtime = rackApplication.getRuntime();

from /Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/config/environment.rb:29:in `load’

javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

(unknown).(unknown)(file:/Applications/apache-tomcat-6.0.18/webapps/quartz_demo/WEB-INF/lib/jruby-rack-0.9.2.jar!/rack/builder.rb:22)
Thanks for every help,
Christian


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Hi Matt,

it look like my attempt to solve the problem. I will try it in the next
days…
At this moment, I looking again for a more ruby like solution, so that
other
ruby people easier can understand it. My current state is following:

RAILS_ROOT/config/initializers/quartz_scheduler.rb

require ‘java’
require ‘lib/commons-logging-1.1.jar’
require ‘lib/commons-collections-3.2.jar’
require ‘lib/commons-pool-1.3.jar’
require ‘lib/quartz-all-1.6.4.jar’

require ‘sharing_stateless_job_factory’
require ‘sharing_stateless_job_detail’

include_class ‘org.quartz.impl.StdSchedulerFactory’
include_class ‘org.quartz.JobDetail’
include_class ‘org.quartz.CronTrigger’

RAILS_DEFAULT_LOGGER.info ‘intializing Quartz Scheduler…’

scheduler = org.quartz.impl.StdSchedulerFactory.getDefaultScheduler
scheduler.setJobFactory SharingStatelessJobFactory.new
scheduler.startDelayed 5

RAILS_DEFAULT_LOGGER.info ‘intializing LoggingJob…’
job = SharingStatelessJobDetail.new(‘LoggingJob’, ‘schedulingGroup’,
LoggingJob.new)
trigger = org.quartz.CronTrigger.new(‘LoggingJobTrigger’,
‘schedulingGroup’,
‘0 * * * * ?’)
scheduler.scheduleJob(job, trigger)

RAILS_DEFAULT_LOGGER.info ‘Finished initializing quartz scheduler and
jobs’

RAILS_ROOT/lib/logging_job.rb

include_class ‘org.quartz.Job’
include_class ‘org.quartz.JobExecutionContext’

class LoggingJob
include org.quartz.Job

def execute context
RAILS_DEFAULT_LOGGER.info “LoggingJob scheduled at #{Time.now}”
end
end

RAILS_ROOT/lib/sharing_stateless_job_detail.rb

include_class ‘org.quartz.JobDetail’
include_class ‘org.quartz.SchedulerException’

class SharingStatelessJobDetail < org.quartz.JobDetail

attr_accessor :job

def initialize(name, group, job)
super()
setName name
setGroup group
@job = job
end

def validate
raise org.quartz.SchedulerException.new(“Job’s name cannot be null”,
org.quartz.SchedulerException.ERR_CLIENT_ERROR) if getName == nil
raise org.quartz.SchedulerException.new(“Job’s group cannot be
null”,
org.quartz.SchedulerException.ERR_CLIENT_ERROR) if getGroup == nil
end
end

RAILS_ROOT/lib/sharing_stateless_job_factory.rb

include_class ‘org.quartz.spi.JobFactory’
include_class ‘org.quartz.spi.TriggerFiredBundle’
include_class ‘org.quartz.JobDetail’

class SharingStatelessJobFactory
include org.quartz.spi.JobFactory

def newJob bundle
jobDetail = bundle.getJobDetail
jobDetail.job
end
end

added
RAILS_ROOT/lib/commons-logging-1.1.jar
RAILS_ROOT/lib/commons-collections-3.2.jar
RAILS_ROOT/lib/commons-pool-1.3.jar
RAILS_ROOT/lib/quartz-all-1.6.4.jar

And look, it works… :slight_smile:

** Rails loaded.
** Loading any Rails specific GemPlugins
** Signals ready. TERM => stop. USR2 => restart. INT => stop (no
restart).
** Rails signals registered. HUP => reload (without restart). It might
not
work well.
** Mongrel 1.1.5 available at 0.0.0.0:3000
** Use CTRL-C to stop.
Dec 8, 2008 10:39:56 PM org.quartz.core.QuartzScheduler start
INFO: Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
LoggingJob scheduled at Mon Dec 08 22:40:00 +0100 2008
LoggingJob scheduled at Mon Dec 08 22:41:00 +0100 2008
LoggingJob scheduled at Mon Dec 08 22:42:00 +0100 2008

This version has a lot of issues:

  • CTRL + C does’t work (you must kill the process) --> implement a
    shutdown hook
  • ensure, we start only one Scheduler per web app
  • many more… :slight_smile:

But I think it is a good starting point for my first rails plugin/gem…

Regards,
Christian

P.S. Sorry for the java code style. I will fix this…