I am using some custom jars in a JRuby on Rails project. All I’m doing
is putting them into the /lib directory and then requiring java and
importing them via my models, etc.
The problem is that I want to do something more complex with these jars
than what I’m capable of with JRuby - I need to be able to write pure
java code to interact with them properly.
So what if I want to have a central .class file or something which I
call from my model, which then interacts with the jars? Is this
possible? Do I need to add my custom class file to one of the jars?
I don’t have a “right” answer for you, but I what I can share with you
is
what has worked for me.
My team uses torquebox and sinatra for our web services.
We started by just directly interacting with Java APIs in our Ruby
classes
but kept bumping into issues with exception handling and type errors.
When
it comes time to deal with incompatible library upgrades, some of the
APIs
are so complex I felt like we were rewriting the application.
We have now tried minimizing/isolating all of our interactions with Java
code in our application. We started writing custom java code that
provides
a narrow interface in order to expose the functionality we need and then
package it up into a shaded jar with dependencies.
As far as delivery is concerned, we used to just deploy the shaded jars
in
vendor/jars but again ran into some issues with respect to managing
those
libraries. A few months ago we decided to move our application
functionality into gems and deliver the shaded jars as part of those
gems.
We hope to take advantage of the jar-dependencies project someday but
haven’t tried it yet.
We also haven’t tried writing custom JRuby extensions so I don’t have
any
feed back there.
So how would I use something like Maven in a Rails app? Would I
separately build a maven java project with all of its dependencies, then
just drop the jar into the lib folder and reference it from JRuby?
I want the simplest solution possible. Basically I have a few open
source jars I want to use in my project, but I feel like I can only
really work with them through java, then calling that class through
JRuby, therefore abstracting any complex java from the Ruby code itself.
First off you don’t want to just compile stuff by hand and use class
files. Use a tool like gradle or maven to compile your java source into
proper jars. I prefer gradle personally. That said you can just dump
your .class file into lib and it will work fine.
lib is the directory that jruby sets up as the classpath. It’s the only
directory you can put jar or class files into and have things work
correctly without using -J-cp to set the classpath yourself on the
command
line. Yes you can put jars in other locations and just do a require on
them, but that can break any dynamic loading that’s done in java
(Class.forName, etc…). So when you find java throwing errors about not
finding jdbc drivers and such, that’s why.
Personally I don’t like just dumping jars in lib, that’s my ruby
directory.
So I set the classpath explicitly and place my jars elsewhere. The
simplest way to do this is to specify a directory ending with /*. Java
will expand that so your classpath is then an explicit list of all files
in
the directory. Shaded or all in one jars make it more acceptable to
just
keep using lib as you then just have a single jar. This is more of a
personal preference thing imo.
After a LOT of trial and error, I found that the best way to run a jruby
program is to have a wrapper. It’s pretty rare you are going to just
use
the default JVM settings for memory and such, and then when you throw
the
classpath settings in you end up with a rather large command line that
is a
pain to have to type in all the time. Following is an example of a
wrapper
for a ruby script (such as a gem binary) that sets JVM options, sets
the
classpath, and passes any additional command line arguments through to
your
ruby script.
FYI don’t use the JVM settings in that script, they are very specific to
that app and non standard.
And for reference, here is a basic gradle project that includes a plugin
to
create all in one jars. It includes gradlew which I highly recommend,
and
the shadowjar plugin for creating all in one jars. Youl can pretty much
just copy that entire directory, remove what’s in src/main/java, change
the
group and project name, and you have a working template to use for your
own
java code. The gradle docs are not the best when it comes to getting
started and showing the big picture, but I found gradle more intuitive
then
maven when coming from ruby.
I had forgotten about ruby-maven. I recall a conversation at one point
where you where describing an experimental feature of the jruby-1.7.14
that
allowed users to specify maven dependencies in gemspecs but can’t seem
to
trac that down. Was that my imagination?