Wednesday, June 01, 2011

Extending JRuby, Compile and Jar Java Extension Code

I wrote about how to extend JRuby by Java in my blog post, Extending JRuby. At that time, I didn't compile Java code since Eclipse performed that automatically. To jar Java code, I used jar command and made an archive manually. However, this is definitely not nice. In general, Java people use maven or ant to package Java code into a single jar. Although maven and ant are among choices to package JRuby extension code, there is a more Ruby-like way of packaging. It is rake-compiler. Nokogiri uses rake-compiler for both CRuby and pure Java versions to compile and package.


Rake-compiler might be known as a cross-compiling tool, but it is also a compiling tool for Java code. Not like maven and ant, we don't write XML files. Instead, we write Rakefile. This means we compile and package Java code by a rake task on JRuby.


Let's get started. First, you need to install rake-compiler gem to your *JRuby* since it is Java code compilation. Make sure you are using JRuby.
jruby -S rake gem install rake-compiler

Next, make sure your Java code is under an *ext* directory. Rake-compiler assumes extension codes are under the *ext* directory in terms of Convention over Configuration. My Eclipse project created a *src* directory for Java sources, so I refactored the name from src to ext on Eclipse. Then, my Rakefile became as in below:
# -*- ruby -*-

require 'rubygems'
require 'rake/javaextensiontask'

Rake::JavaExtensionTask.new('commons/math/fraction') do |ext|
jruby_home = ENV['MY_RUBY_HOME'] # this is available of rvm
jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar']
ext.classpath = jars.map {|x| File.expand_path x}.join ':'
ext.name = 'commons/math/poplar'
end

To compile Java code for JRuby extension, requiring 'rake/javaextensiontask' and writing Rake::JavaExtensionTask are all you need. The question would be what should be in JavaExtensionTask. The constructor argument specifies the directory Java code resides. The jruby_home is to know where jruby.jar is. jars is just an array to set ext.classpath effectively. You may assign whole classpath directly to ext.classpath variable. If you are on Windows, you need to change classpath delimiter from ':' to ';' I also specified ext.name parameter. Without this, JavaExtensionTask creates an jar archive, "lib/commons/math/fraction.jar" from the constructor arguments. In my case, this name is confusing since I have lib/commons/math/fraction.rb, too. Once JRuby finds fraction.rb, JRuby happily quits searching loop. So, lib/commons/math/fraction.jar won't be loaded. You can also specify other parameters like Ruby exntesion, for example,
  • ext.gem_spec
  • ext.tmp_dir
  • ext.lib_dir
  • ext.platform
  • ext.config_options
  • ext.source_pattern
For Java extension, we can set
  • source_version
  • target_version
The default values of these parameters are 1.5.

Everything is ready. Let's compile and jar Java extension code. On your terminal, type
rake compile

Just this compiles and jars Java extension code. The created jar archive is lib/commons/math/poplar.jar. This directory is natural for JRuby extension gems. You can see other rake tasks from rake -T. On your terminal, rake clean, rake clobber and two compile tasks will show up. Next time, you might type
rake clean
rake compile


Like above, rake-compiler works to compile and jar JRuby Java extension code. This might be more familiar and better to J/Rubyists.


All of my code are on GitHub, https://github.com/yokolet/Poplar

Sunday, May 15, 2011

Rubinius on JRuby … ?

Of course, compiled Rubinius binary, *.rbc, file doesn’t work on JRuby. This post is about JRuby’s rubinius branch. I’m not sure how may people are aware that, but JRuby does have a rubinius branch. As far as I looked at that branch, it is not to be merged into master, at least, in near future. Maybe it is headius’ pet project at this moment, and is implemented as a JRuby extension. Yes, it is a JRuby extension. The branch attempts “extending JRuby,” like I wrote about in my blog post (http://yokolet.blogspot.com/2011/05/extending-jruby.html). Since this extension is still small, it would be a good practice to figure out how extension works. So, I’m going to write how you can decipher existing JRuby extension. Hopefully, this will help you to write your own extension.


To try the rubinius branch, you have two preparations to be done. The first one is to build JRuby of the rubinius branch. It is easy. Just clone out JRuby, checkout rubinius branch, and run ant command as in below:

git clone git://github.com/jruby/jruby.git
cd jruby
git checkout rubinius

ant clean-all
ant

Next, you need Rubinius source. You don’t need to build rubinius just to see how it works. But, the rubinius extension uses Rubinius’ kernal sources, which is included only in the source archive. That’s why you need the source. Rubinius source archive is available to download from http://rubini.us/. Get the archive and unzip it.


Up until now, you had rubinius branch JRuby and Rubinius source. Set the environment variables. Suppose the JRuby’s home directory is /Users/yoko/Projects/jruby, then, set JRUBY_HOME and PATH like in below. Adjust them to fit in to your system:

export JRUBY_HOME=/Users/yoko/Projects/jruby
PATH=$JRUBY_HOME/bin:$JRUBY

Suppose, Rubinius source are in /Users/yoko/Projects/rubinius-1.2.2, then set RBX_KERNEL as in below:

export RBX_KERNEL=/Users/yoko/Projects/rubinius-1.2.2/kernel



Everything should be ready. Let’s try this out.

bash-3.2 rubinius$ jruby -S irb
irb(main):001:0> require 'rubinius'
=> true

Yay! Rubinus was successfully loaded on JRuby. What’s next? Look at the RubiniusLibrary.java (https://github.com/jruby/jruby/blob/rubinius/src/org/jruby/ext/rubinius/RubiniusLibrary.java). At the line 51, “Rubinius” module is defined.

RubyModule rubinius = runtime.getOrCreateModule("Rubinius");

So, there should be a constant, Rubinius.

irb(main):002:0> Rubinius
=> Rubinius
irb(main):003:0> Rubinius.class
=> Module

So far, so good. Then, at the line 56, you can see

RubyTuple.createTupleClass(runtime);

This means you should go to RubyTuple.java (https://github.com/jruby/jruby/blob/rubinius/src/org/jruby/ext/rubinius/RubyTuple.java). On the line 55-62 of RubyTuple.java, createTupleClass method is defined. In this method, "Tuple" class is defined under the "Rubinius" module. Then, annotated methods, which have Java annotation @JRubyMethod, are defined. Looking at the rest of the code in RubyTuple.java, you can see three annotated methods (new, [], and []=), and one override method (dup) are there. Let’s try these.

irb(main):013:0> Rubinius::Tuple
=> Rubinius::Tuple
irb(main):014:0> tuple = Rubinius::Tuple.new 3
=> #<Rubinius::Tuple:0x3df89785>

This constructor needs one argument because rbNew method is defined as:

public static IRubyObject rbNew(ThreadContext context, IRubyObject tupleCls, IRubyObject cnt) {

Here's the rule. First two arguments are given internally, and the rest of the arguments are given from users. So, I typed “3” as an argument. Let’s keep going on.

irb(main):018:0> tuple[0]
=> nil
irb(main):019:0> tuple[0]=123
=> 123
irb(main):020:0> tuple[0]
=> 123
irb(main):021:0> tuple_dup = tuple.dup
=> #
irb(main):022:0> tuple_dup[0]
=> 123

All right, methods worked.


Next, get back to RubiniusLibrary.java and let’s look at the lines 84-88.

runtime.getObject().deleteConstant("Hash");
runtime.getLoadService().lockAndRequire(rbxHome + "/common/hash.rb");
RubyClass hash = (RubyClass)runtime.getClass("Hash");
hash.defineAnnotatedMethods(RubiniusHash.class);
runtime.setHash(hash);

Soooo interesting! Could you figure out what’s going on here? Hash is redefined using Rubinius code! JRuby’s Hash is entirely written in Java (https://github.com/jruby/jruby/blob/rubinius/src/org/jruby/RubyHash.java). But, once “require rubinius” is done, the Hash is totally replaced by Rubinius’ Hash, which IS written in Ruby. See? JRuby can be extended also in Ruby, not just Java. To do double check this, let’s add one line in initialize method of kernel/common/hash.rb:

def initialize(key, key_hash, value)
@key = key
@key_hash = key_hash
@value = value
@next = nil
puts "Rubinius Hash!!" # this line is added
end

Then, restart irb and re-request rubinius.

bash-3.2 rubinius$ jruby -S irb
irb(main):001:0> require 'rubinius'
=> true
irb(main):002:0> h = Hash.new
Rubinius Hash!!
=> {}

Yay, Hash is really Rubinius’ Hash. What an idea!


As I wrote, you have a lot of options for “extending JRuby.” You can extend using mature Java APIs and, also, cutting edge Ruby code. Why don’t you try this fantastic extension? It should be fun.

Monday, May 02, 2011

Extending JRuby

As pure Java Nokogiri does, we can extend JRuby writing a library backed by Java API. Other than Nokogiri, Weakling (https://github.com/headius/weakling), Warbler(https://github.com/nicksieger/warbler), JSON(https://github.com/flori/json) and more are examples of JRuby extension by Java. If you use google code search with a keyword, "BasicLibraryService," you'll find some more gems. This BasicLibraryService is a sign that the gem is implemented by Java. BasicLibraryService is an interface and has just one method, basicLoad(Ruby runtime). Simple. However, questions might come up in people's mind. What should I write in basicLoad method? How is it called? Not many answers are out there. The helpful answer I could find was a comment on LoadService.java (or LoadService19.java for 1.9 mode). But, it would be still short to write a JRuby extension for JRuby users who want to write their own. So, I wrote a sample code to see how JRuby can be extended. This sample is quite a simple one and far from real JRuby extensions such as pure Java Nokogiri or others. But, the first thing is to understand how it works. This sample will help to get started.


Before going deeper, let's look at a usage of Java API directly from JRuby. I chose Apache Commons Math API (http://commons.apache.org/math/). This API is interesting. It makes many mathematical calculations easy and natural. Among them, I picked up a fraction package. Everybody knows. In Japan, elementary school kids study how to add, subtract or common denominator, etc. It should be easy, but neither Java or Ruby doesn't have such API in a standard library.

Below is a JRuby code that uses fraction Java API directly. This code adds up reciprocals of 1 to 4, Harmonic series of n = 4. The answer is obvious, 1/1 + 1/2 + 1/3 + 1/4 = 25/12.
require 'java'
$: << '/Users/yoko/Tools/commons-math-2.2'
require 'commons-math-2.2'

java_import org.apache.commons.math.fraction.Fraction

f = Fraction.new(1, 1)
(2..4).each do |i|
f = f.add(Fraction.new(1, i))
end
puts f

My commons-math-2.2.jar is in /Users/yoko/Tools/commons-math-2.2 directory, so I added that path to $LOAD_PATH, then, required that jar archive. The .jar suffix is optional when requiring something on JRuby. JRuby searches from every possible paths adding .class, .rb, .jar or .bundle suffixes. Next, I imported Fraction class and calculated in a straightforward way.

Let's think how this code can be improved to more Ruby like one. Ruby programmer might like f.add!(something) rather than f = f.add(something). So, in this JRuby extension sample, I implemented "add!" method.

Firstly, I wrote FractionService class, which implements BasicLibraryService interface. But, wait. API design should come in before starting it because XXXService class works based on convention over configuration. Java's package name and Ruby's module structure must coincide. In my design, the Fraction class is Commons::Math::Fraction::Fraction in Ruby. This means FractionService class should be in a commons.math.fraction package, and require statement in Ruby should be "require 'commons/math/fraction/fraction.' This is how basicLoad() method is called.

Next would what we should write in basicLoad() method. In general, defining module structures/classes and object allocators are done in this method. Ola Bini's blog, "The JRuby Tutorial #4: Writing Java extensions for JRuby" (http://ola-bini.blogspot.com/2006/10/jruby-tutorial-4-writing-java.html) would be worth to read how to write the method. My simple FractionService became as in below:
package commons.math.fraction;

import java.io.IOException;

import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.BasicLibraryService;

public class FractionService implements BasicLibraryService {

@Override
public boolean basicLoad(Ruby runtime) throws IOException {
RubyModule commons = runtime.defineModule("Commons");
RubyModule math = commons.defineModuleUnder("Math");
RubyModule fractionModule = math.defineModuleUnder("Fraction");
RubyClass fraction = fractionModule.defineClassUnder("Fraction", runtime.getObject(), FRACTION_ALLOCATOR);
fraction.defineAnnotatedMethods(Fraction.class);
return true;
}

private static ObjectAllocator FRACTION_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new Fraction(runtime, klazz);
}
};
}


Then, I wrote commons.math.fraction.Fraction class. In this class, I defined "add!" and "to_s" methods. We can't use "!" in a method name in Java, so the Java method name is add_bang instead. Ruby method name is define in @JRubyMethod annotation. Also, I wrote Ruby's constructor method "new," which is "rbNew" method in Java. The "new" method should be a class method, so it is a static method in Java. Annotations of methods are important. Three annotated *JRubyMethods* in Fraction class get fired up by fraction.defineAnnotatedMethods(Fraction.class); in FractionService class. Because of this, we can use Java methods in Ruby. See my Fraction class below:
package commons.math.fraction;

import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyObject;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Arity;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

@JRubyClass(name="Commons::Math::Fraction")
public class Fraction extends RubyObject {
private org.apache.commons.math.fraction.Fraction j_fraction = null;

@JRubyMethod(name="new", meta = true, rest = true)
public static IRubyObject rbNew(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
Fraction fraction = (Fraction) ((RubyClass)klazz).allocate();
fraction.init(context, args);
return fraction;
}

public Fraction(Ruby runtime, RubyClass klass) {
super(runtime, klass);
}

void init(ThreadContext context, IRubyObject[] args) {
Arity.checkArgumentCount(context.getRuntime(), args, 2, 2);
int numerator = (Integer) args[0].toJava(Integer.class);
int denominator = (Integer) args[1].toJava(Integer.class);
j_fraction = new org.apache.commons.math.fraction.Fraction(numerator, denominator);
}

org.apache.commons.math.fraction.Fraction getJFraction() {
return j_fraction;
}

@JRubyMethod(name = "add!")
public IRubyObject add_bang(ThreadContext context, IRubyObject other) {
if (other instanceof Fraction) {
org.apache.commons.math.fraction.Fraction other_fraction = ((Fraction)other).getJFraction();
j_fraction = j_fraction.add(other_fraction);
return this;
} else {
throw context.getRuntime().newArgumentError("argument should be Commons::Math::Fraction type");
}
}

@JRubyMethod
public IRubyObject to_s(ThreadContext context) {
return context.getRuntime().newString(j_fraction.toString());
}
}



I haven't written Rakefile for packaging at this moment, so I manually create jar archive of two Java classes.
jar -J-Duser.language=en -cvf ../lib/commons/math/poplar.jar commons



OK, all Java classes are ready for my simple JRuby extension, so let's work on Ruby code. We might have an option to require Java classes directory, but that is not nice. Since users themselves must require FractionService or other, internal change will affect users code. Besides, it doesn't look like Rubygems. So, I wrote commons_math_fraction.rb to hook up FractionService.
require 'commons-math-2.2'
require 'commons/math/fraction'

Surely, this code needs to be brush up, for example, paths. But, I kept simple since this sample is to understand the idea of extending JRuby.
Then, one more Ruby code, commons/math/fraction.rb:
require 'commons/math/poplar'
require 'commons/math/fraction/fraction'

module Commons
module Math
module Fraction
end
end
end

The first line requires poplar.jar archive, and the second does FractionService class.


Everything is ready, so let's write Ruby code using this tiny, shiny JRuby extension. The file name is fraction_sample.rb:
require 'java'

$: << '/Users/yoko/Documents/workspace/Poplar/lib'
require 'commons_math_fraction'
f = Commons::Math::Fraction::Fraction.new(1, 1)
(2..4).each do |i|
f.add!(Commons::Math::Fraction::Fraction.new(1, i))
end
puts f

Since my JRuby extension is not yet gem, GEM_PATH or other gem loading ways don't work. I set a load path to my *Poplar* project. Under the lib directory, I have jars and Ruby files.
jruby fraction_sample.rb

gave me the answer 25 / 12.


Like this, we can extend JRuby using Java API. Using Java API under the hood, we can create Rubygems. Some are re-implementation by Java like pure Java Nokgoiri. Others are Java originated Ruby API. JRuby extension is an example that Java effectively complements Ruby. So, add Ruby API to your favorite Java tools.


All codes of this sample are https://github.com/yokolet/Poplar.

Monday, April 25, 2011

Attempt to get Nokogiri work on Android


Conclusion


As a result, Nokogiri was loaded on Android successfully but didn't work on it. When I tried to parse XML document, I got tons of errors something like:
W/dalvikvm(  374): Unable to resolve superclass of Lorg/apache/xerces/dom/DeferredDocumentImpl; (2008)
W/dalvikvm( 374): Link of class 'Lorg/apache/xerces/dom/DeferredDocumentImpl;' failed

I'm pretty sure this sort of error messages complain there aren't enough interfaces of org.w3c packages defined in Android SDK. Actually, Android SDK's org.w3c API is a subset of JDK's. This is the problem. Xerces needs a full-set of org.w3c packages to work. Pure Java Nokogiri heavily relies on Xerces and nekoHTML/nekoDTD, which are built on top of Xerces. So, pure Java Nokogiri also needs the fullset of org.w3c packages to keep compatibility with libxml2 backed, CRuby version. This is why Nokogiri ended up in raising an exception as in below:
W/dalvikvm(  374): threadid=10: thread exiting with uncaught exception (group=0x40014760)
E/AndroidRuntime( 374): FATAL EXCEPTION: runWithLargeStack
E/AndroidRuntime( 374): java.lang.NoClassDefFoundError: org.apache.xerces.dom.DeferredDocumentImpl
E/AndroidRuntime( 374): at org.apache.xerces.parsers.AbstractDOMParser.startDocument(Unknown Source)
E/AndroidRuntime( 374): at org.apache.xerces.impl.dtd.XMLDTDValidator.startDocument(Unknown Source)
(snip)

Is this avoidable? Might be. Googling led me some discussions about replacing org.w3c and related other packages. If I can include Xerces' xml-apis.jar (this defines org.w3c/org.w3c.xxx, javax.xml.xxx, org.xml.xxx) in my Android app and override some of core packages, Nokogiri will start working exactly the same as a web app on Rails. But, it should not be a good workaround. Surgery on SDK might incur other applications that use replaced packages.


Probably, the best answer will create a subset of Nokogiri for Android. I'm not sure such limited version of Nokogiri still attracts users. But, I think it's better than nothing.



Thoughts on Ruboto and Android

Although my small Nokogiri app didn't work, I'm going to write about what I learned and did. This might help some poeple who want to make Ruby gems to work.

  • JDK should be 1.6.0_24 on OS X
Ruboto people might not develop JRuby on Rails on Google App Engine, but I do. Just before I tried Ruboto, I had to downgrade JDK version for Google App Engine gem. So, when I started, my JDK was 1.6.0_22. I spent pretty much time to figure out why ruboto didn't work on my PC at all. Once the JDK got back to the latest, ruboto worked like a magic. Make sure what version of JDK you are using.


  • Android API level should be 11
Not all Ruboto samples needs level 11 API. For example, samples of https://www.ibm.com/developerworks/web/library/wa-ruby/ worked on level 8. But, Nokogiri needs level 11. I'm not sure the reason, but, the activerecord (and jdbc) sample, https://github.com/ruboto/ruboto-core/wiki/Tutorial%3A-Using-an-SQLite-database-with-ActiveRecord-and-RubyGems, was also tested on level 11, which is Java backed rubygems like Nokogiri.


  • Jar archives should be moved to project's libs directory
This happens on an environment that uses custom classloader, for example, Google App Engine. So, I have all jars in my project's libs directory, https://github.com/yokolet/cranberry/tree/master/libs, so that custom classloader can load all jars. If those jars failed to be loaded, Nokogiri raises a mysterious, "undefined method `next_sibling' for class `Nokogiri::XML::Node'," error. I didn't get that error, so jars should be loaded.

Also, I commented line 18-24 out from nokogiri.rb (https://github.com/yokolet/cranberry/blob/master/assets/vendor/gems/1.8/gems/nokogiri-1.5.0.beta.4-java/lib/nokogiri.rb) so that Nokogiri doesn't try to load those jars again.


  • Configuration and setup are key to load gems
Loading gems on Ruboto was tricky. In the article, https://www.ibm.com/developerworks/web/library/wa-ruby/, the author rearranged all ruby files into single directory. This might work for small rubygems but never does for Nokogiri. For example, Nokogiri has nokogiri/html/document.rb and nokogiri/xml/document.rb. Instead, the way described in https://github.com/ruboto/ruboto-core/wiki/Tutorial%3A-Using-an-SQLite-database-with-ActiveRecord-and-RubyGems worked well. It looks complicated, but I realized that the thread based gem loading way was really necessary while I was trying other stuff. My config.rb is https://github.com/yokolet/cranberry/blob/master/assets/scripts/config.rb if you want look at it. Also, I edited src/irg/ruboto/Script.java (https://github.com/yokolet/cranberry/blob/master/src/org/ruboto/Script.java) and added "vendor" directory.

When I clicked on "Cranberry" Ruboto icon right after "rake install" said "Success," all Nokogiri files were copied to /data/data/.... directory. To cut down the time for copying, I deleted Nokogiri's test and ext directories, which are unnecessary to run Nokogiri.


  • Needs threads to become a nifty app
Android expects developers' "responsiveness" (http://developer.android.com/guide/practices/design/responsiveness.html). According the document, database or network access should not be performed on a main thread. In my Nokogiri sample, I tried to get rss feed, on the main thread firstly, so I got the error:
W/System.err(  343): org.jruby.exceptions.RaiseException: Native Exception: 'class android.os.NetworkOnMainThreadException'; Message: null; StackTrace: android.os.NetworkOnMainThreadException
W/System.err( 343): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1077)
W/System.err( 343): at java.net.InetAddress.lookupHostByName(InetAddress.java:481)
(snip)

This is why config.rb uses threads to require rubygems.


  • No need to reinstall app when scripts are updated
"rake update_scripts" updates Ruby scripts of installed app. So, you don't need reinstall the app. This was a great help for me since an installing process took many many minutes.


  • ... but, it doesn't work. What's going on ???
As an Android newbie, I very often fell into troubles to get Android SDK and the app to work. Sometimes, app icons didn't show up, or rake install failed. The troubleshooting, https://github.com/ruboto/ruboto-core/wiki/Troubleshooting, was so helpful. Especially, "adb kill-server; adb start-server" commands were the best. Also, I made a rule to type "ruby -v" before I started something. As you know, rake tasks start working on CRuby. But, those won't complete tasks as you expect.

I'd like to add "uninstall" the app to the troubleshooting. You can uninstall the app on emulator as well as adb uninstall command. On the emulator, do the long-click on the icon you want to uninstall. Then, trash bin and the word "uninstall" appears. Dragging the icon on trash bin will delete the app. Or adb uninstall [package name of app] will delete the app. For example, my app's package name is com.servletgarden.ruboto.cranberry, so "adb uninstall com.servletgarden.ruboto.cranberry" deleted my app from emulator. In case you forget the package name, look at the path to XXXActivity.java file. That path corresponds to package layer.



How I made this app

In the end, I'm going to add how I made this app and how to start it. This app won't work, but for myself, to try this app in future again, I'll leave this memo.

1. install ruboto-core gem
  $ ruby -v    (double check I'm on JRuby)
$ gem install ruboto-core

2. set path to android tools
  $ cd path/to/android-sdk-mac_x86
$ PATH=`pwd`/tools:`pwd`/platform-tools:$PATH

3. create emulator image
  $ android -s create avd -f -n cranberry-11 -t android-11
This possibly creates it. Actually, I created my virtual image using Eclipse's ADT. It's way easy. Prior to using android command, I installed platforms. I also used Eclipse's ADT for that.


4. create ruboto app
  ruboto gen app --package com.servletgarden.ruboto.cranberry --target android-11
This generated cranberry directory and whole stuff under that.


5. add emulator task to Rakefile
  $ cd cranberry
$ [edit Rakefile]
(line 42-45 of https://github.com/yokolet/cranberry/blob/master/Rakefile)
Since my virutal image name is cranberry-11 (step 3) "-avd cranberry-11" is there. If the app is small, you don't need -partition-size option.


6. install nokogiri gem
  $ mkdir -p assets/vendor/gems/1.8
$ gem install --install-dir assets/vendor/gems/1.8 nokogiri -v 1.5.0.beta.4
$ rm -rf assets/vendor/gems/1.8/cache
$ rm -rf assets/vendor/gems/1.8/doc
$ pushd assets/vendor/gems/1.8/gems/nokogiri-1.5.0.beta.4-java
$ rm -rf ext test
$ popd

7. add config.rb file, one line in assets/scripts/cranberry_activity.rb and edit Script.java
line 2 of https://github.com/yokolet/cranberry/blob/master/assets/scripts/cranberry_activity.rb
line 186-188 of https://github.com/yokolet/cranberry/blob/master/src/org/ruboto/Script.java


8. start emulator
  $ rake emulator
The emulator took many minutes to boot up on my MacBook. Occasionally, it showed up without dark blue hexagons. In such case, emulator didn't work correctly. I tried a couple of times "adb kill-server" and "adb start-server." When that attempt didn't work, I shut the emulator down and did "adb kill-server," then restarted the emulator.


9. start log monitor
  $ adb logcat

This prints out verbose infos, errors, and others. It is a bit noisy, but a great help to figure out what's going on.


10. install app
  $ rake install
Be patient.


10. click Cranberry ruboto icon
Be patient again. JRuby needs long time to activate.
Ruboto default app, Figure 4 of https://www.ibm.com/developerworks/web/library/wa-ruby/ will show up.



11. edit ruby files and do "rake update_scripts"
Then, back to Apps view and clock ruboto icon. Updated version should work, or troubleshooting time starts.


Whew...!

Thursday, April 14, 2011

Nokogiri on Google App Engine

Nokogiri 1.5.0 is on its way right now. Sure, it should be soonish. This version is also the first release of pure Java Nokogiri. We call it *pure Java*, but the name might not express itself precisely. Since it is written half Ruby and half Java, so *pure JRuby* (pragdave called so) would be the best name. This pure JRuby version implements methods, which are implemented in C, using xerces, nekoHTML, jing and a couple more Java Tools, while CRuby version uses libxml and libxslt. When people use Nokogiri 1.5.0 on JRuby, they use pure Java version.
What's the beauty of pure Java Nokogiri? It works smoothly on various platforms if Java runs on them. On OS X, Linux, Windows, and even Google App Engine, Nokogiri starts working painlessly. Really frequently asked questions for Nokogiri are "I can't install Nokogiri," or "Nokogiri doesn't work." Definitely, pure Java Nokogiri doesn't have these problems.


To see pure Java Nokogiri works fine, I gave it a try on Google App Engine (GAE). As you know, GAE supports python or Java only. Using libxml is out of scope. In short, pure Java Nokogiri just worked. Easy. (Unexpectedly, I struggled to get GAE work, so I'll write how I made it.) Although I don't have many to write about, I'm going to note what I did for people who don't know they can use Nokogiri on GAE.


First, I installed gems following the instruction, https://gist.github.com/825451. The instruction says, "Do not use rvm," but, I used rvm. Using rvm is not the matter. Rubygems' version is the matter. After I installed Ruby 1.8.7 using rvm, I downgraded rubygems to 1.3.7. Don't forget, google-appengine gem needs version 1.3.7 (or before) of rubygems. Otherwise, bundler08 will fail to install gem command *bundle*. This will end up in raising an error when appengine gem tries to install gems in .gems/bundler_gems/jruby/1.8/gems directory. Make sure *bundle* is listed in there when you type "gem help commands." See http://groups.google.com/group/appengine-jruby/browse_thread/thread/2db62b1a51896098 for a detail.

You do need to have CRuby but don't need to install JRuby. Appengine gem will install jruby-jar gem when it is needed. The gem, jruby-jar, has JRuby's stdlib in a jar archive. JRuby gets stared using this jar archive. So, google-appengine gem mostly works on CRuby and uses jruby-jar gem when JRuby is needed. Therefore, all gems should be installed on CRuby. Below is what I did.

rvm 1.8.7
sudo gem install google-appengine (Since I installed rvm to /usr/local, I need *sudo*)
sudo gem install rails -v 2.3.11
sudo gem install rails_dm_datastore
sudo gem install activerecord-nulldb-adapter
mkdir rails_app; cd rails_app
curl -O http://appengine-jruby.googlecode.com/hg/demos/rails2/rails2311_appengine.rb
ruby rails2311_appengine.rb

Then, rails app is ready to run. To start app on a development server,

./script/server.sh

This should start Jetty and rails app on that.

However, I was among unlucky people. I got Segmentation fault because my Java was Java SE 6 Update 4 for Mac OS X. Googling, I followed "Comment 39" of http://code.google.com/p/googleappengine/issues/detail?id=4712. I didn't want to downgrade JDK, but there seemed no better choice. Anyways, rails app successfully worked on update 3.


Next, I added Nokogiri in Gemfile. Currently 1.5.0.beta.4 is the latest.

gem 'nokogiri', '1.5.0.beta.4'

One more. The latest version of jruby-jar gem is 1.6.1, but, sadly, the jar archive in the gem is too big to upload. JRuby 1.6.1 grew bigger. As far as I remember, 1.6.0 is also too big to upload. Again, downgrade came in. I used version 1.5.6, and my Gemfile became as in below:

# Critical default settings:
disable_system_gems
disable_rubygems
bundle_path '.gems/bundler_gems'

# List gems to bundle here:
gem 'rails_dm_datastore'
gem 'jruby-jars', '1.5.6'
gem 'jruby-openssl'
gem 'jruby-rack', '1.0.5'
gem 'rails', '2.3.11'
gem 'nokogiri', '1.5.0.beta.4'



OK, my platform has been ready. Let's create a simple Nokogiri sample. In this sample, I got the rss feed from cnn.com (http://rss.cnn.com/rss/cnn_topstories.rss), parsed it using Nokogiri, and displayed news list. Since this is just a simple sample of Nokogiri, I generated a controller only.

./script/generate controller newsfeeds index

The rss I used was like https://gist.github.com/921058. From this XML document, I collected item elements using xpath. Then, I extracted pubDate, title, link, and description children elements of item also using xpath.

# newsfeeds_controller.rb
require 'nokogiri'
require 'open-uri'

class Entry
attr_reader :title, :url, :description, :pubdate
def initialize(title, url, description, pubdate)
@title = title
@url = url
@description = description
@pubdate = pubdate
end
end

class NewsfeedsController < ApplicationController
def index
doc = Nokogiri::XML(open("http://rss.cnn.com/rss/cnn_topstories.rss"))
items = doc.xpath("//item")
@entries = []
items.each do |item|
title = item.xpath("title").text
url = item.xpath("link").text
description = item.xpath("description").text
pubdate = item.xpath("pubDate").text
@entries << Entry.new(title, url, description, pubdate)
end
end
end

# newsfeeds/index.html.erb
<h1>Newsfeeds#index</h1>
<% @entries.each do |entry| %>
<dl>
<dt><%= entry.pubdate %></dt>
<dt><b><%= entry.title %></b> [<%= link_to("Read", entry.url) %>]</dt>
<dt><%= entry.description %></dt>
</dl>
<% end %>

When I restarted the server./script/server.h and requested http://localhost:8080/newsfeeds/, I could see news list something like this.



The last thing I did was uploading. I set my application id on the line "application:" in WEB-INF/app.yaml, then uploaded it by ./script/publish.sh. Now my Nokogiri sample is working at http://4.latest.servletgarden-in-red.appspot.com/newsfeeds/.


In the end, I'm going to add a link to the blog talked about Nokogiri on Google App Engine. This would be helpful, too.

- Google App Engine, JRuby, Sinatra and some fun!


So far, pure Java Nokogiri worked just fine on Google App Engine. Give it a try!

Thursday, March 03, 2011

RedBrdige's Sharing Variables, How It Works

RedBridge's sharing variables feature is convenient to let objects back and forth between Java and Ruby. The feature makes things easy but might be hard to understand a bit. The idea of sharing variables itself is simple. On Java side, all variables to share are saved in an internal map. When Ruby code is parsed/evaluated, all variables in the map are injected to Ruby runtime. All variables (local vars are slightly different) used in Ruby code are eligible to be retrieved after the evaluation. When the variable is requested to be retrieved, the value is grabbed from Ruby runtime (lazy mode). Or, when the evaluation finishes, all variables used in Ruby code are retrieved and saved in the map on the Java side (non-lazy mode). However, it depends on choices of both local context type and local variable behavior. Also, it is Ruby's receiver and scope aware. Besides, there were bugs, which have made people confused. (I've fixed several bugs in this area in JRuby 1.6.0RC1/RC2/RC3) So, I'll write about how it should work for clarity.


1. Lazy or non-Lazy mode

This option works for retrieving variables from Ruby runtime and is independent from any local context type and local variable behavior. It specifies whether only requested variable is retrieved on demand or all variables are retrieved every time evaluation finishes. As you may realize easily, the former has better performance. Yes, this option has been added to improve performance. By default, lazy mode is on for embed core (ScriptingContainer), and off for JSR223 (I changed the default of JSR223 between RC2 and RC3. On RC3 and later, non-lazy is default for JSR223). JSR223 has javax.script.SimpleBindings/SimpleScriptContext for a variable holder, which makes it very hard to work on demand. For JSR223 users, it is important that JRubyEngine works in the same way as other ScriptEngines such as Rhino, BeanShell, etc. So, I took compatibility over performance. Still, JSR223 users can choose lazy mode, but they should know some trick makes available to get a variable value from Ruby runtime.

This figure illustrates how sharing variables of lazy mode works: The *BiVariableMap* in the figure is the internal map to save variables. The internal map is responsible for a type coercion from Java to Ruby and from Ruby to Java. Moreover, based on a given name, BiVariableMap chooses a right variable type such as a global, or instance variable and its logic to get from/set to Ruby runtime. As in the figure, only when a user want to get a variable, the variable is grabbed form Ruby runtime on demand. While the variable is returned to the user, it is also created in the BiVariableMap for a possible later use.



Next figure illustrates one of a non-lazy retrieving sequence on JSR223: This would be a typical way on JSR223. Users are allowed to instantiate JDK bundled classes javax.script.SimpleBindings/SimpleScriptContext. Unfortunatelly, RedBridge has no way to hook over these classes to make sharing variables work. Besides, there is no way to know what variable a user want to retrieve from Ruby runtime after the evaluation. So, when the evaluation finishes, JRubyEngine tries to get all variables/constants that look user defined ones. In some cases, BiVariableMap grows pretty fatty. But, when evaluation ends, all possible variables must exists in SimpleBindings/SimpleScriptContext. To implement JSR223 faithfully, JRubyEngine sacrificed performance and memory saving.




Well, how people can change lazy mode? I'll show just JSR223 example since ScriptingContainer users are happy with the default, lazy mode, and won't feel inconvenience.

For comparison, I'll put an example of default settings here:

// non-lazy mode; default
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("rb");
SimpleBindings bindings = new SimpleBindings();
engine.eval("$weather = 'freezing rain'; $temperature = '28F'", bindings);
System.out.println("It should be '28F': " + bindings.get("temperature"));

This code prints
"It should be '28F': 28F"

The following is a lazy setting example:

// lazy mode on
System.setProperty("org.jruby.embed.laziness", "true");
engine = new ScriptEngineManager().getEngineByExtension("rb");
bindings = new SimpleBindings();
engine.eval("$weather = 'snow'; $temperature = '17F'", bindings);
System.out.println("It should be null: " + bindings.get("temperature"));
System.out.println("It should be '17F': " + engine.get("temperature"));

Above prints:

It should be null: null
It should be '17F': 17F

So, when engine's get method is used, retrieving a variable on demand works. Or, more Ruby way would work. For example, Ruby can return more than one variables at the same time. Returned values are saved in an Array, which is converted to java.util.List:

List list = (List) engine.eval("$weather = 'sleet', $temperature = '32F'");
System.out.println("It should be 'sleet': " + list.get(0));
System.out.println("It should be '32F': " + list.get(1));

When this snippet gets run, it prints

It should be 'sleet': sleet
It should be '32F': 32F



2. Singleton Type

Before going forward, let's review local context types one by one. To make it clear, I wrote figures that illustrate the structures of each type in terms of sharing variables. The figures will help you to understand what are going on.


The first type is singleton. This is a default type for both ScriptingContainer and JRubyEngine. The singleton type has only one Ruby runtime on JVM, which is *singleton* as the name expresses. BiVariableMap ("Var Map" in the figure) is also only one on JVM. No matter how many instances of ScriptingContainer / JRubyEngine you create, there is only one set of the runtime and variable map. In this type, thread safety is users' responsibility. No synchronization in API is RedBridge's policy.


3. SingleThreaded Type

The second type is naive singlethread. This is the simplest type and good to test something simple. This singlethreaded model would be a typical one that other JSR223 engines adapt to. The singlethreaded type can have multiple sets of Ruby runtimes and BiVariableMaps on a single JVM. If you instantiate three ScriptingContainers / JRubyEngines, you'll have three sets of runtime and variable map on the JVM. Again, thread safety is users' responsibility.


4. Threadsafe Type

The third type is threadsafe. In this type, a set of runtime and variable map is a thread local value. Thus, each *thread* has its own set of runtime and variable map. This type allows us to isolate an internal state by creating a thread. Just one instance of ScriptingContainer / JRubyEngine creates multiple sets of runtime and variable map along with threads. Users don't need to worry about thread safety as long as the concerned threads are created in Java. The thread safety here doesn't mean Ruby threads.

Here's the example of threadsafe type that isolates the internal state. The code is here.

import java.util.Map;

import org.jruby.Ruby;
import org.jruby.embed.LocalContextScope;
import org.jruby.embed.LocalVariableBehavior;
import org.jruby.embed.ScriptingContainer;

public class TransientThreadsafe {

private TransientThreadsafe() {
ScriptingContainer container =
new ScriptingContainer(LocalContextScope.THREADSAFE, LocalVariableBehavior.TRANSIENT);
Runner runner1 = new Runner(container);
Runner runner2 = new Runner(container);
new Thread(runner1, "Runner-1").start();
new Thread(runner2, "Runner-2").start();
runner1.getVarMap().put("$tmp", "Atlanta");
runner2.getVarMap().put("$tmp", "Los Angeles");
}

public static void main(String[] args) {
new TransientThreadsafe();
}

class Runner implements Runnable {
ScriptingContainer container;
Map varMap = null;

Runner(ScriptingContainer container) {
this.container = container;
}

@Override
public void run() {
varMap = container.getProvider().getVarMap();
while (varMap == null || varMap.get("$tmp") == null) {
try {
Thread.currentThread().sleep(1000L);
} catch (InterruptedException e) {
// no-op
}
}
container.runScriptlet("puts \"" + Thread.currentThread().getName() + " ran in #{$tmp}\"");
}

Map getVarMap() {
while(varMap == null) {
try {
Thread.currentThread().sleep(1000L);
} catch (InterruptedException e) {
// no-op
}
}
return varMap;
}
}
}

From the output below, we can see two different sets of runtime and variable map are there:

Runner-2 ran in Los Angeles
Runner-1 ran in Atlanta


5. Concurrent Type

The last type is concurrent. This type is added in 1.6.0RC1 and mixture of singleton and threadsafe. Concurrent type has singleton runtime and thread local variable map. Probably, it is the most complicated type but works well in some cases. For example, gems are evaluated in Java Servlet's init() method, then, classes and methods of those gems can be used in doGet()/doPost()/etc methods. On a Servlet container, each HTTP request is on a thread, so each HTTP request can have an isolated state.


6. Transient Local Variable Behavior

OK, let's see local variable behavior types one by one. We have transient (default for ScriptingContainer), persistent, global (default for JSR223), and bsf. I'm not going to talk about bsf type. It is just for BSF engine, which is almost obsolete.

The first local variable behavior is transient. The transient type is natural to Ruby. When we assign value "hello" to a variable name "$message," it is $message = "hello" in Ruby. We can use Ruby's global, instance, local variables and constants to share between Java and Ruby.

When you use global variables to share, you need to care what local context type you are using. Because the global variables are global on a runtime, the variable becomes unique on the single runtime. When the runtime is singleton (singleton and concurrent types), a global variable, say $tmp, is shared globally. We have two more to care about. One is that embedding API doesn't see what type of variable is given. We can put a global variable with a receiver object, but the given receiver is ignored when the global variable gets pushed on to the runtime. Another is that the global variables persist in BiVariableMap unless they are deleted. As long as global variables are in BiVariableMap, they are re-injected to runtime. When the global variables are removed from BiVariableMap, nil is set as its value on Ruby side. (There's no way to delete global variables in JRuby)

How about instance variables? When you use instance variables, you need care about they are in top level or some object. If it is a top level instance variable, the variable will be unique on a single runtime. Because the "top self" object is the only one on the runtime, the instance variable of the top self object is the only one. When you put instance variables using embedding API without a receiver, they will be injected as top level variables. This behavior is the same on all four local context types. The persistence of instance variables on Java side is the same as global variables. Unless they are deleted from the map, they will keep being injected to the runtime.

Constants works exactly the same as instance variables, so I won't add anything about constants.

The behavior of transient local variables are remarkable. The local variables on Java side vanish from a variable map after each evaluation. Because they are the local variables, they should not survive over the evaluation. Suppose 'orange' gem is evaluated right after 'red' gem is evaluated. What if both RubyGems use a local variable name "tmp"? The 'red' gem might change the value of 'tmp' local variable. Ruby code never expects the value of 'tmp' is changed by another gem and given for evaluation. Thus, the local variables in the map are removed not to cause unexpcted results when evaluation finishes. If you want to use the same local variable again, put it to ScriptingContainer / JRubyEngine(or SimpleBindings/SimpleScriptContext) again. So far, sharing *local* variable is available only for a top level local variable.


7. Persistent Local Variable Behavior

Next local variable behavior is persistent. As the name shows, this type makes local variables persistent. The local variables survive over the evaluations like global and instance variables. This behavior was added mainly for ex-BSF users. BSF defines both local and global variables should persist on an engine. Ex-BSF users feel this behavior essential. This type might look useful, but users themselves need to avoid local variables collision.

Other than local variables, global variables, instance variables and constants work exactly the same as transient type.


8. Global Local Variable Behavior

The last local variable behavior is global. The name, global local variable, might sound weird, but it actually expresses the behavior. If we put the key-value pair, container.put("tmp", 100) in Java side, it will be $tmp = 100 in Ruby side. The variable looks like local or constants in Java, but everything is a global variable in Ruby. So, we can use only global variables and need to care what local context type is used. As I wrote in "6. Transient Local Variable Behavior," the global variables are shared globally on a single runtime.

This type is for JSR223. JSR223 reference implementation released from Sun was this type, so embedding API had the same local variable behavior for compatibility. Moreover, to fulfill JSR223 specification, values of local global type variables on Ruby side go to nil when the evaluation finishes but not on Java side.

This global local type is really weird in terms of Ruby programming. But, the design of Ruby language is very different from other JVM languages. Besides, not all JSR223 users are dedicated Ruby programmers. This type might be the answer for JSR223 to avoid unexpected results.


9. No Sharing Variables

In the end, you might not need sharing variables feature to work. You are probably a big fun of ScriptingContainer's callMethod() or JSR223's invokeMethod() / invokeFunction(). Every variable to get Ruby code work are given through method's arguments. In fact, this has better performance than repeating runScriptlet() or eval(). Embedding API does have the option to off the feature.

[ScriptingContainer]
container.setAttribute(AttributeName.SHARING_VARIABLES, false);

[JSR223]
System.setProperty("org.jruby.embed.sharing.variables", "false");




This is how RedBridge's sharing variables works. Various field of users have requested various kinds of features to embedding API. To cover those as much as possible, embedding API has become a bit complicated. If you understand how it works, you'll feel comfortable to use in the way that fits you the best. Have happy coding with embedding API.

Sunday, February 27, 2011

Rails on RedBridge, Scaffolded App to Work

In my blog post, "The second step to Rails on RedBridge", I used Rails simple controller and showed how it could get run on Servlet. That was easy since there was no interaction with a Web browser. That controller just returned a result to the browser. That's it. So, Rails didn't need much information to work. However, a controller-only application isn't common. People start using Rails from scaffolding. Every Rails book talks about scaffolding first. Thus, I tried to make scaffolded Rails app to work on the Servlet. So far so good. Though it was a newbie app, scaffolding has been covered by Rails on RedBridge. The scaffolded app worked via JRuby's embedding API on the Servlet. The code is on github, https://github.com/yokolet/RailsCrossing, and the sample app by Scala is also on github, https://github.com/yokolet/RailsCrossingSamples.

The way to make it work was a bit hard. Rails API doc was not so helpful in terms of hooking it up on the Servlet. To figure out how it should have worked, I used rails console a lot, read sources, watched what were supposed to be in HTTP requests/responses over FireBug, wrote snippets to see what was missing. So, I'm going to write down how RailsCrossing (Rails on RedBridge) made scaffolded app to work.

The idea of RailsCrossing is to hit "SomeController.action('some_action').call(env)" method. Thus,

  1. Find the controller class
  2. Find the action
  3. Stuff all necessary information into "env" hash

are all to make it work.


Finding controller class is not so complicated. For example, we can confirm routes mapping on rails console:

irb(main):001:0> ActionDispatch::Routing::Routes.routes.to_s
=> "GET /Acacia/home/index(.:format) {:controller=>\"Acacia/home\", :action=>\"index\"}GET /Acacia/users(.:format) {:action=>\"index\", :controller=>\"users\"}POST /Acacia/users(.:format) {:action=>\"create\", :controller=>\"users\"}GET /Acacia/users/new(.:format) {:action=>\"new\", :controller=>\"users\"}GET /Acacia/users/:id/edit(.:format) {:action=>\"edit\", :controller=>\"users\"}GET /Acacia/users/:id(.:format) {:action=>\"show\", :controller=>\"users\"}PUT /Acacia/users/:id(.:format) {:action=>\"update\", :controller=>\"users\"}DELETE /Acacia/users/:id(.:format) {:action=>\"destroy\", :controller=>\"users\"}ANY /rails/info/properties(.:format) {:controller=>\"rails/info\", :action=>\"properties\"}"

Odd thing was the scope name "Acacia" appeared in a controller name when I created controller by "generate controller" but not by "generate scaffold." Although I surrounded whole stuff by a scope "/Acaia," routes.rb, it happened. I'm not sure this is intended or a bug, but, anyways, I took the scope name out from to find controller class if it is included.


Finding an action needed closer look at a Rails behavior. As you may know, Rails RESTful app uses GET, POST, PUT, and DELETE HTTP requests for index/show/new/edit, create, update, and destroy actions respectively. Even if the given path is the same, say /Acacia/users/1, it is for show when HTTP method is GET while update when PUT. Rails uses "_method" hidden HTML form field to know the difference. So, I added lines to get that parameter from HTTP request, then put in env hash with a "rack.methodoverride.original_method" key. Also the _method value was used to find out matched action and controller combination.
(See getEnv() and findMatchedRoute() methods in CrossingHelpers.java)


Stuffing all necessary information in the "env" hash was rather baffling. JRuby's Java proxy didn't work well since Rails expected hash keys could be referenced as a symbol. For example, a hash is created by h = {"name" => "foo"}, it should be referenced by h[:name]. JRuby's Java String proxy seemed not to be effective for this usage. To avoid this problem, I directly used JRuby's RubyHash class.

Next problem was keys to assign input parameters. "rack.input" is mandatory, but, as far as sending HTML form parameters, "rack.input" isn't used for that purpose. It is used to compare the value of "rack.request.form_input," and not used afterward as well as "rack.request.form_input." (If input is multipart/form-data (file upload), "rack.input" is used to read binary data, though.) Instead, "rack.request.form_hash" key is used to send input values. There's one more key-value pair involved in sending form parameters. It is "action_dispatch.request.path_parameters." The id, like user id, seems to be expected to be there. I put the hash got by Rails.application.routes.recognize_path("some path") as a value of ""action_dispatch.request.path_parameters." The example of a hash value related to sending form parameters became as in below:

"action_dispatch.request.path_parameters" => {:action=>"show", :controller=>"users", :id=>"1"}
"rack.input" => ""
"rack.request.form_input" => ""
"rack.request.form_hash" => {"utf8"=>"✓", authenticity_token"=>"fi/oOym8i+mZTwM7+h+rZBQL/s9hv62+
mnNRv6mNQnw=", "user"=>{"name"=>"foo", "email"=>"foo@bar"}, "commit"=>"Update User"}


Moreover, there are "flash" messages, such as green "User was successfully updated." Those also should be in "env" hash. This sort of messages are in a HTTP response returned as a result of form input processing. When the response is redirected and back to Rails app, the messages show up to browser. To keep the messages over a sequence of HTTP request/response has been completed, we need cookie like mechanism. Rails uses cookie to save such messages. On Servlet, we can use Servlet API's session, which is available to save an object. So, I took the hash for flash massages out from the response, then put it in Servlet's session without any modification.

String script =
"response = " + route.getName() + ".action('" + route.getAction() + "').call(env)\n" +
"return response[0], response[1], response[2].body, response[2].request.flash";
RubyArray responseArray = (RubyArray)container.runScriptlet(script);
CrossingResponse response = new CrossingResponse();
response.context_path = context_path;
response.status = ((Long)responseArray.get(0)).intValue(); //status code; Fixnum
response.responseHeader = (Map)responseArray.get(1); // response header; Hash
response.body = (String)responseArray.get(2); // response body; HTML
response.flash = (Map) responseArray.get(3); // flash messages; Hash

....

request.getSession().setAttribute("action_dispatch.request.flash_hash", crossingResponse.getFlash()); //save falsh messages to Servlet's session

....

// before sending a request to Rails app
Map map = (Map) request.getSession().getAttribute("action_dispatch.request.flash_hash");
if (map != null) env.put("action_dispatch.request.flash_hash", map);



Serving static assets, such as stylesheets and javascripts, needed to have another way. Even though I added the scope in routes.rb, Rails didn't add the scope name to static assets not like paths to displayed in HTML body part. There should have the Rails way to add some path to static assets always, but I didn't search that. Because I wanted to keep Rails app as much as it is. Instead, RailsCrossing rewrites the URI so that the Servlet context path (the same as the Rails scope name) will be in the URI just before the response is sent back to the browser. RailsCrossing has its own default Servlet (CrossingDefaultServlet.java) to serve such static files. This is necessary so that Servlet Container dispatches the HTTP request to Acacia web app. Unless, the request is dispatched to a Servlet Container's default Servlet that doesn't know where the files are.


Above are what I tried to get scaffolded Rails app to work. Probably, there are the right ways to get this done, but I couldn't find by googling. (I want to know where I should go.)


RailsCrossing is still on the long way to serve Rails app satisfactory, but getting closer and closer. As I wrote, simple scaffolded Rails app has been covered. Next would be Ajax request handling, file upload, complicated database schema, etc. ... maybe, I need to work more.

Tuesday, February 15, 2011

Using Rails from Scala

Using Rails from Scala? Yes, it is possible if you use RailsCrossing (https://github.com/yokolet/RailsCrossing). This is a following chapter of my previous blog post, The second step to Rails on RadBridge. As I wrote "I'll try Rails from Scala next." at the end of that blog post, I tried it today. How did it go? It worked just fine. Here's how I used Rails from Scala.


1. Preparation of Java web application project and Rails app

These are exactly the same as the previous post. See steps 1 through 3. This time, I named Java web application "Sycamore," but the rest of all are the same.


2. Scala Servlet for Rails

Writing Servlet by Scala is really easy. I read the article, The busy Java developer's guide to Scala: Scala and servlets for that and could easily follow. I don't explain about what is Scala Servlet because that's not my purpose. So, it's good for you to google or goto some sites to learn what it is.

Here's the Scala Servlet that initializes Rails app and dispatches HTTP request to Rails app:

package com.servletgarden.sycamore

import javax.servlet.ServletConfig
import javax.servlet.http.{HttpServlet, HttpServletRequest => HSReq, HttpServletResponse => HSResp}
import com.servletgarden.railsxing.CrossingServlet

class RailsScalaServlet extends CrossingServlet {
override def init(config: ServletConfig) = super.init(config)

override def doGet(request: HSReq, response: HSResp) = dispatch(request, response)

override def doPost(request: HSReq, response: HSResp) = dispatch(request, response)
}

Compare RailsScalaServlet above and SimpleSample Servlet in the previous post. Basically, both Servlet do the same thing. See the previous post for details about what those two servlets are doing.

That's it.


3. Scala Servlet Compilation

Before executing Scala Servlet, the Servlet needs to be compiled. The article mentioned above, "The busy...," explains how to do that. Also, Scala Ant Tasks section of Developer's Guides will help you to understand how to write build.xml. I'm using NetBeans and its Java web application projects, so I directly edited NetBeans' build.xml. My build.xml is https://gist.github.com/828420. It assumes the directory tree below:

Sycamore -+- src -+- java -+- com -+- servletgarden -+- sycamore -+- RailsScalaServlet.scala
+- web -+- META-INF -+- context.xml
+- WEB-INF -+- lib -+- Gemfile
| +- Gemfile.lock
| +- RailsCrossing.jar
| +- blog -+- Gemfile
| | +- README
| | +- Rakefile
| | +- ...
| +- jruby-complete.jar
| +- jruby -+- 1.8 -+- bin -+- bundle
| | | +- erubis
| | | +- ....
| | +- cache -+- ...
| | +- doc
| | +- gem -+- abstract-1.0.0 -+- ..
| | +- gem -+- .... -+- ..
| | +- gem -+- bundler-1.0.10 -+- ..
| | +- gem -+- .... -+- ..
| | +- specifications -+- ...
| +- scala-compiler.jar
| +- scala-library.jar
|
+- index.jsp

In my case, NetBeans' build menu compiles Scala files and puts compiled classes under Sycamore/build/web/WEB-INF/classes directory.


4. Configurations

SimpleSample Servlet in the previous post uses annotation to configure the Servlet, thus, no web.xml is there. On the other hand, RailsScalaServlet uses web.xml to set rail_path and gem_path, and path mapping. The web.xml file is https://gist.github.com/828572.

Double check the context name of this Java web application has been changed to "blog." See "5. One More Setup" of the previous post.


5. Run Scala Servlet to use Rails

After starting tomcat, request http://localhost:8080/blog/home/index from a web client. Like in the previous post, familiar Rails default page will show up.



As I wrote how to here, using Rails from Scala is relatively easy. If you want to mix Scala code and Rails results, you can use methods of CrossingHelpers bypassing CrossingServlet. Also, you can use JRuby's ScriptingContainer to take Rails classes out to call methods of them. Try this new Rails way.

Monday, February 14, 2011

The second step to Rails on RedBridge

This is the second attempt to make Rails work on RedBrdige (JRuby embedding API). I believe Rails on RedBridge got closer to a real application. The first attempt is also in this blog, A small step to Rails on RedBridge. The blog post had some impact on a few people who want to control Rails (or Sinatra) from Java. The example there successfully showed how we could wake Rails up from Java Servlet, but was almost a hello world example. Meanwhile, I demonstrated simple Rails app from Groovy (Groovy Servlet, precisely) in my session at RubyConf 2010, "RubyGems to All JVM Languages." Rails on RedBridge way is an approach from Java API on Servlet, so all JVM languages are possibly available to use Rails *gem* from them over JRuby's Java API. That demonstration at RubyConf 2010 impressed the audiences there enough to have a clap, but I knew it was still a hello world example. Recently, I tried to develop Rails on RedBridge more so that Rails app got work like other rackup style frameworks. RailsCrossing is the outcome. It is the Java API to use Rails.


OK. I'm going to write how to setup and write code using RailsCrossing.


1. Creating Java Web Application Project

The first job is to create a Java web application project. I used NetBeans for that. You can use whatever IDE you like that has the feature to create a Java Servlet based web application project. My web app project has a name "Spruce" and the server to run it is Tomcat 7.0.4.

Initially, a directory tree of the Spruce project looks like below (I didn't write NetBeans specific files/directories):

Spruce -+- src
+- web -+- META-INF -+- context.xml
+- WEB-INF
+- index.jsp


The directory tree is slightly different when other IDEs are used. That's not the matter. Just look at WEB-INF directory. The WEB-INF directory is special for Java Servlet web app. It is the place to put libraries and is protected from accesses originated from web clients.


2. Prepare for Rails APP

RailsCrossing assumes Rails app is located under WEB-INF directory. It could not be necessarily there. However, I made that constraint since Java Servlet web application should be portable. Everything needed to the web app work should be in the web app directory tree. It's the policy.

Let's prepare the app.

First, get jruby-complete.jar and put it in WEB-INF/lib directory. (Get the latest (master) branch of JRuby. Or, you can use jruby-complete.jar of RailsCrossing project)

Spruce -+- src
+- web -+- META-INF -+- context.xml
+- WEB-INF -+- lib -+- jruby-complete.jar
+- index.jsp


Next, install bundler gem. Make sure bundler will be installed under WEB-INF directory. So, let's go to lib directory and type as in below (you can use jruby command instead):

$ java -jar jruby-complete.jar -S gem install bundler --no-ri --no-rdoc -i jruby/1.8
Fetching: bundler-1.0.10.gem (100%)
Successfully installed bundler-1.0.10
1 gem installed

Don't forget -i option to install bundler gem under lib directory. The command above installs bundler gem in WEB-INF/lib/jruby/1.8. So, you can find bundle command in the path, WEB-INF/lib/jruby/1.8/bin/bundle.

Spruce -+- src
+- web -+- META-INF -+- context.xml
+- WEB-INF -+- lib -+- jruby-complete.jar
| +- jruby -+- 1.8 -+- bin -+- bundle
| +- cache -+- ...
| +- doc
| +- gem -+- bundler-1.0.10 -+- ..
| +- specifications -+- ...
|
+- index.jsp


Let's set GEM_PATH to use bundler to install other gems.
$ export GEM_PATH=[path to lib directory]/jruby/1.8


Write or create Gemfile. If you want to use bundle command rather than open a new file by editor, type as in below:
java -jar jruby-complete.jar -S jruby/1.8/bin/bundle init

Gemfile should be something like this:

# A sample Gemfile
source "http://rubygems.org"

gem "rails"

I just deleted the comment sign (#) on the rails line after bundle init command. The last preparation is to install rails gem.
java -Xmx500m -jar jruby-complete.jar -S jruby/1.8/bin/bundle install --path .

Don't forget "--path ." to install gems under the same path as bundler has been installed in. When bundle install command successfully finished, bunch of gems are in jruby/1.8.

Spruce -+- src
+- web -+- META-INF -+- context.xml
+- WEB-INF -+- lib -+- Gemfile
| +- Gemfile.lock
| +- jruby-complete.jar
| +- jruby -+- 1.8 -+- bin -+- bundle
| | +- erubis
| | +- ....
| +- cache -+- ...
| +- doc
| +- gem -+- abstract-1.0.0 -+- ..
| +- gem -+- .... -+- ..
| +- gem -+- bundler-1.0.10 -+- ..
| +- gem -+- .... -+- ..
| +- specifications -+- ...
|
+- index.jsp



3. Create Rails App

Probably, I don't need to say anything. I typed below:
java -jar jruby-complete.jar -S jruby/1.8/bin/rails new blog --template http://jruby.org

Now, blog Rails app is created under WEB-INF/lib/blog.

Spruce -+- src
+- web -+- META-INF -+- context.xml
+- WEB-INF -+- lib -+- Gemfile
| +- Gemfile.lock
| +- blog -+- Gemfile
| | +- README
| | +- Rakefile
| | +- ...
| +- jruby-complete.jar
| +- jruby -+- 1.8 -+- bin -+- bundle
| | +- erubis
| | +- ....
| +- cache -+- ...
| +- doc
| +- gem -+- abstract-1.0.0 -+- ..
| +- gem -+- .... -+- ..
| +- gem -+- bundler-1.0.10 -+- ..
| +- gem -+- .... -+- ..
| +- specifications -+- ...
|
+- index.jsp


In general, database setup follows. But, to make it understandable to know how it works, let's create a controller.

$ cd blog (Now, I'm in Spruce/web/WEB-INF/lib/blog directory)
$ java -Xmx500m -jar ../jruby-complete.jar ../jruby/1.8/bin/bundle install --path ../. (Don't forget --path ../. option!)
$ java -jar ../jruby-complete.jar script/rails g controller home index

So far so good. These steps to creating Rails app should be familiar with.


4. Simple Sample Servlet to run Rails' controller

Here comes RailsCrossing API. You can write Servlet using methods in CrossingHelpers or subclassing CrossingServlet. The CrossingServlet does everything to run Rails' controller. For example, SimpleSample below would be the simplest example.

package com.servletgarden.spruce;

import com.servletgarden.railsxing.CrossingServlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
*
* @author Yoko Harada
*/
@WebServlet(name="SimpleSample",
urlPatterns={"/*"},
initParams={@WebInitParam(name="rails_path", value="/lib/blog"), @WebInitParam(name="gem_path", value="/lib/jruby/1.8")})
public class SimpleSample extends CrossingServlet {

/**
* @see Servlet#init(ServletConfig)
*/
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}

/**
* Processes requests for both HTTP GET and POST methods.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

dispatch(request, response);
}

/**
* Handles the HTTP GET method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Handles the HTTP POST method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Returns a short description of the servlet.
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "the simplest example used RailsCrossing";
}

}


Let's look at what's going on in detail.

In the SimpleSample init method, it calls init method of a super class, CrossingServlet. In CrossingServlet init method, initialization of ScriptingContainer (JRuby embedding API a.k.a. RedBridge) and Rails are done. Then, Rails routes mappings are parsed.

public void init(ServletConfig config) throws ServletException {
container = CrossingHelpers.initialize(config);
routes = CrossingHelpers.parseRoutes(container);
}

While initialization is going on CrossingServlet uses "rails_path" and "gem_path" initParams relative to WEB-INF directory. These are necessary to fire up Rails.


Well, how Rails routes mapping can be parsed? Probably, many of you have used rails console. RailsCrossing does the same thing on a Servlet as people do on rails console. For example,

$ java -jar ../jruby-complete.jar script/rails c
irb(main):007:0> route_ary = ActionDispatch::Routing::Routes.routes
=> [#<ActionDispatch::Routing::Route:0x555669ae @name="home_index"...(snip)
irb(main):004:0> route_ary[0].to_a[1]
=> {:path_info=>/\A\/home\/index(?:\.([^\/.?]+))?\Z/, :request_method=>/^GET$/}
irb(main):005:0> route_ary[0].to_a[2]
=> {:controller=>"home", :action=>"index"}
irb(main):006:0> route_ary[0].to_a[3]
=> "home_index"

CrossingHelpers.parseRoutes method exactly evaluates these commands to draw mapping info out from Rails.


Let get back to SimpleSample Servlet. Look at the processRequest method. In this method, CrossingServlet's dispatch method is called. Just that. What's this dispatch?
Here it is:

protected void dispatch(HttpServletRequest request, HttpServletResponse response) throws IOException {
CrossingRoute route = findMatchedRoute(request.getContextPath(), request.getPathInfo(), request.getMethod());
if (route == null) return;
Map env = getEnvMap(request);
CrossingResponse crossingResponse = CrossingHelpers.dispatch(container, route, env);
response.setStatus(crossingResponse.getStatus());
Set keys = crossingResponse.getResponseHeader().keySet();
for (String key : keys) {
String value = crossingResponse.getResponseHeader().get(key);
response.setHeader(key, value);
}
PrintWriter writer = response.getWriter();
writer.write(crossingResponse.getBody());
}

Based on the parsed routes, CrossingServlet finds what controller should be used from path_info in HttpServletRequest. If matched controller is found, it creates a map of each HTTP request params including HTTP request header and query string. Then, dispatching. Here, RailsCrossing does rails console commands on Servlet again.

irb(main):006:0> env = {"rack.input" => "", "REQUEST_METHOD" => "GET"}
=> {"rack.input"=>"", "REQUEST_METHOD"=>"GET"}
irb(main):008:0> HomeController.action('index').call(env)[0]
=> 200
irb(main):009:0> HomeController.action('index').call(env)[1]
=> {"Content-Type"=>"text/html; charset=utf-8", "ETag"=>"\"a5e60d2fa2208dc316b0f09cef107bba\"", "Cache-Control"=>"max-age=0, private, must-revalidate"}
irb(main):010:0> HomeController.action('index').call(env)[2].body
=> "<!DOCTYPE html>\n<html>\n<head>\n <title>Blog</title>\n...

Once CrossingServlet gets the response, it sets HTTP response status code and header params, then writes html part out to Servlet's writer.

These are what RaisCrossing does.


5. One More Setup

The difference of a context path lies between Java web app and Rails app. In general, Java web application is referenced by http://servername:port/context_name/servlet_name/path_info, while rails is done by http://servername:port/path_info. We need to fix these to make them coincide.

Let's change Rails first editing config/routes.rb:

Blog::Application.routes.draw do
scope "/blog" do
get "home/index"
...
end
end

I added scope in config/routes.rb. Now, blog app is referenced by http://servername:port/blog/path_info. Next is a tomcat setting. I edited META_INF/context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/blog"/>

Originally, it was path="/Spruce" since I named the web app Spruce. After Spruce has been changed to "blog," every HTTP request to http://servername:port/blog/* has been directed to SimpleSample Servlet.


6. Run Servlet

Since I created web app on NetBeans, I can get SimpleSample Servlet started from NetBeans menu with the path, "/home/index" If not, start tomcat up and request "http://localhost:8080/blog/home/index" You'll see familiar controller default output on a browser.


7. Thoughts

RailsCrossing uses Rails metal introduced in version 3. This rack style invocation was easy to get it run. However, it is mostly undocumented area. I attempted many commands on rails console and tried to find the best way; however, there might be still better way.

A good side of RailsCrossing is ... it is a Java API. This means, JVM languages such as Scala, Groovy, or other can use RailsCrossing over java integration feature. I'll try Rails from Scala next.

Wednesday, January 12, 2011

Embedding API refinements for JRuby 1.6.0RC1

TThe biggest release ever --- JRuby 1.6.0RC1 has been released on Jan. 10, 2011. Headius wrote a blog about this biggest-ever release, JRuby 1.6.0 RC1 Released. Ruby 1.9 compatibility, performance improvement, built-in graph profiler, and more. How about embedding API (RedBridge)? In the release note (http://jruby.org/2011/01/10/jruby-1-6-0-rc1.html), you'll find a line mentioned about RedBridge, "Embedding API refinements." I'm going to write what refinements RedBridge had in JRuby 1.6.0RC1 in this blog post.


1. New Context Instance Type, concurrent

So far, RedBridge has had three types of context models, singleton, singlethread, and threadsafe. JRuby 1.6.0 added one more type, concurrent. The concurrent type is somehow mixture of singleton and threadsafe. Ruby runtime is a singleton, variables (except global variables) are thread local. This model would be good to use RedBridge on servlet based web applications. For example, we can instantiate Ruby runtime and evaluate all Ruby codes during servlet’s initialization, then use them in HTTP request processing methods, such as doGet() or doPost(). Threadsafe context model is similar, but Ruby runtimes are on each thread (each HTTP request on a web app). On the other hand, the runtime in the same classloader will be used on the concurrent context model on all threads, if Ruby runtime has been already initialized by another app.

The concurrent model is a bit complicated and assuming users have enough knowledge about Java web application and Ruby's variable/constant scope. Global variables are totally unprotected from concurrent processing since Ruby runtime is a singleton. As you may know the scope of a global variable is global on Ruby runtime, so is the concurrent context. Global variables of concurrent model are looked up from all threads (all HTTP requests on a web app) simultaneously. The scope of other variable types and a constant depends on how those are used. If those are top-level variables/constants, the scope will be global since Runtime is a singleton. If not, those are thread local and protected from concurrent processing.

When you use the concurrent context model, specify as in below:

Embed core
ScriptingContainer container = new ScriptingContainer(LocalContextScope.CONCURRENT);

JSR223
System.property(“org.jruby.embed.localcontext.scope”, “concurrent”);



2. Receiver aware sharing variables

I’ve written about this new feature in the blog post, http://yokolet.blogspot.com/2010/09/new-featues-of-embedding-api-for-jruby.html ScriptingContainer.get()/put()/remove() methods had a receiver object in those method arguments. If the receiver object is not specified, runtime top-self is used as a receiver. This means those operations are not thread safe on concurrent context type. Be careful.
Also, see the lazy variable retrieval feature of the same blog post. This is for performance improvement.


3. Classloader option for JSR223

The new option to set classloader into ScriptingContainer via ScriptEngineFactory has been added from JRuby 1.6.0RC1. This is for users who want to use JSR223 ScriptEngine for JRuby on some custom classloader. Setting a classloader of ScriptingContainer itself to Ruby runtime is for now well-known technique on custom classloader. The problem was JSR223 doesn’t have a method for that. So, JRuby 1.6.0 sets the classloader that is used to instantiate ScriptingContainer to Ruby runtime by default. This is done in ScriptEngineFactory#getScriptEngine() method internally. This behavior can be disabled by a system property, org.jruby.embed.classloader.

System.setProperty(“org.jruby.embed.classloader”, “current”); // default, sets classloader
System.setProperty(“org.jruby.embed.classloader”, “none”); // doesn't set classloader


The option is JSR223 only. The org.jruby.embed.classloader system property has no effect when you directly instantiate ScriptingContainer. Set whatever appropriate classloader using ScriptingContainer.setClassLoader() method.


4. New Map proxy

This is not a embedding API but JRuby core. Also the feature is not only for RedBridge users but also for all JRuby users. However, the new proxy would make RedBridge users happy especially, so I’ll add this feature here.

Since JRuby 1.6.0RC1, a java.util.Map type object has all Ruby Hash methods by default. Let’s see example code.

ScriptingContainer container = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
ConcurrentHashMap map1 = new ConcurrentHashMap();
map1.put("a", 100);
map1.put("b", 200);
Map map2 = new HashMap();
map2.put("b", 254);
map2.put("c", 300);
container.put("map1", map1);
container.put("map2", map2);
String script =
"map1.merge!(map2) {|k,o,n| o+n }\n" +
"puts \"#{map1.inspect}, length = #{map1.length}\"\n" +
"puts \"class: #{map1.class}\"";
container.runScriptlet(script);

Set entries = map1.entrySet();
for (Map.Entry entry : entries) {
System.out.print(entry.getKey() + ": " + entry.getValue() + ", ");
}

Above code prints:

{"a"=>100, "b"=>454, "c"=>300}, length = 3
class: Java::JavaUtilConcurrent::ConcurrentHashMap
a: 100, b: 454, c: 300,

Two Map type objects were merged as instructed, map1.merge!(map2) {|k,o,n| o+n }. Also in Java world, map1 was changed exactly the same way as was in Ruby world.


These are the brief summary of embedding API refinement done in JRuby 1.6.0RC1. Have fun and please report if you find a bug or encounter a weird behavior.

Friday, October 22, 2010

Gems in a Jar with RedBridge

Using JRuby embed API (RedBridge), it is easy to use Ruby gems from Java. However, packaging might not be simple, so people occasionally struggle to create a "portable" package. Being portable is important for a Java app. All stuffs of the Java app should be packaged in a jar archive, which should work on another PC, or even different OS. This is the good side of Java. This blog illustrates one solution for packaging.

I intentionally didn't use jruby command since the command hides what are going on behind. You might be exhausted by lines of "java -jar ....," sorry. But, it helps to understand a packaging process.

Directories


Since I used bundler to install gems, I created directories below to fit them to bundler.

Linden -+- lib -+- jruby -+- 1.8
+- src -+- linden
+- build

"Linden" is a top directory and the name doesn't have any special meaning. You can name it whatever you like. "lib/jruby/1.8" is the directory gems will be installed. "src" is for Java code, and "build" is for compiled *.class files.

Bundler Installation


I intentionally used jruby-complete.jar to assure gems won't be installed in the default location. The path to jruby-complete.jar can be anything since typing a full path to jruby-complete.jar works. But, I like a short path to type, so I copied jruby-complete.jar under "Linden/lib." Now, the directories/files were as in below:

Linden -+- lib -+- jruby -+- 1.8
+- jruby-complete.jar
+- src -+- linden
+- build


Then, the bundler installation went:

cd lib
java -jar jruby-complete.jar -S gem install bundler jruby-openssl --no-ri --no-rdoc -i jruby/1.8

Bundler and jruby-openssl gems were installed and the directories became as in below:

Linden -+- lib -+- jruby -+- 1.8 -+- bin -+- bundle
| +- cache -+- ...
| +- doc
| +- gems -+- bouncy-castle-java-1.5.0145.2 -+- ...
| | +- bundler-1.0.3 -+- ...
| | +- jruby-openssl-0.7.1 -+- ...

| +- specifications -+- ...
+- jruby-complete.jar
+- src -+- linden
+- build

Let's see whether gems are really installed.

export GEM_PATH=`pwd`/jruby/1.8
java -jar jruby-complete.jar -S gem list

This command should print out the installed three gems and pre-installed gems.

*** LOCAL GEMS ***

bouncy-castle-java (1.5.0145.2)
bundler (1.0.3)
columnize (0.3.1)
jruby-openssl (0.7.1)
rake (0.8.7)
rspec (1.3.0)
ruby-debug (0.10.3)
ruby-debug-base (0.10.3.2)
sources (0.0.1)

OK. Bundler was installed successfully.

Installation of other gems


Next step is to install other gems using bundler, so I used bundle command. Theoretically, PATH environment variable should work to find bundle command by ruby. Unfortunately, this didn't work in my "java -jar ..." usage. Instead, I typed a path to bundle command after -S option.

java -jar jruby-complete.jar -S jruby/1.8/bin/bundle init

Then, I added "twitter" gem to Gemfile.

# A sample Gemfile
source "http://rubygems.org"

# gem "rails"

gem "twitter"

The installation went on:

java -Xmx500m -jar jruby-complete.jar -S jruby/1.8/bin/bundle install --path=.

The java command option "-Xmx500m" is for performance and to avoid memory outage. Now, 6 gems were installed and the directories/files became:

Linden -+- lib -+- jruby -+- 1.8 -+- bin -+- bundle
| | +- httparty
| | +- oauth

| +- cache -+- ...
| +- doc
| +- gems -+- bouncy-castle-java-1.5.0145.2 -+- ...
| | +- bundler-1.0.3 -+- ...
| | +- crack-0.1.8 -+- ...
| | +- hashie-0.4.0 -+- ...
| | +- httparty-0.6.1 -+- ...

| | +- jruby-openssl-0.7.1 -+- ...
| | +- multi_json-0.0.4 -+- ...
| | +- oauth-0.4.3 -+- ...
| | +- twitter-0.9.12 -+- ...

| +- specifications -+- ...
+- jruby-complete.jar
+- Gemfile
+- Gemfile.lock

+- src -+- linden
+- build


Java code to use twitter gem


Since all gems were ready, I wrote Java code to see gems worked. This time, I relied on GEM_PATH environment variable to find gems. The fist code had Java package, linden, and class name, SearchSample. So, I created SearchSample.java file under "src/linden" directory.

package linden;

import org.jruby.embed.LocalContextScope;
import org.jruby.embed.ScriptingContainer;

public class SearchSample {
private String jarname = "sample.jar";

private SearchSample() {
String basepath = System.getProperty("user.dir");
System.out.println("basepath: " + basepath);
ScriptingContainer container = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
System.out.println("jrubyhome: " + container.getHomeDirectory());
container.runScriptlet("ENV['GEM_PATH']='" + basepath + "/lib/jruby/1.8'");

String script =
"require 'rubygems'\n" +
"require 'twitter'\n" +
"require 'pp'\n" +
"pp Twitter::Search.new('#jruby').fetch.results.first";
container.runScriptlet(script);
}

public static void main(String[] args) {
new SearchSample();
}
}

Linden -+- lib -+- jruby -+- 1.8 -+- bin -+- bundle
| | +- httparty
| | +- oauth
| +- cache -+- ...
| +- doc
| +- gems -+- bouncy-castle-java-1.5.0145.2 -+- ...
| | +- bundler-1.0.3 -+- ...
| | +- crack-0.1.8 -+- ...
| | +- hashie-0.4.0 -+- ...
| | +- httparty-0.6.1 -+- ...
| | +- jruby-openssl-0.7.1 -+- ...
| | +- multi_json-0.0.4 -+- ...
| | +- oauth-0.4.3 -+- ...
| | +- twitter-0.9.12 -+- ...
| +- specifications -+- ...
+- jruby-complete.jar
+- Gemfile
+- Gemfile.lock
+- src -+- linden -+- SearchSample.java
+- build



Compile and run using rake-ant integration



Everything was ready. OK, how do I compile and run it? Of course, classic "javac" and "java" command were the options. But, these commands are not very convenient to repeat compile/run. So, I used JRuby's rake-ant integration. Yes, I wrote "Rakefile" to compile and run Java code. Isn't it nice? Here's Rakefile:

require 'ant'

namespace :ant do
task :compile => :clean do
ant.javac :srcdir => "src", :destdir => "build"
end
end

namespace :ant do
task :java => :compile do
ant.java :classname => "linden.SearchSample" do
classpath do
pathelement :location => "lib/jruby-complete.jar"
pathelement :path => "build"
end
end
end
end

require 'rake/clean'

CLEAN.include '*.class', '*.jar'

I created Rakefile under the Linden directory, so now the directories/files were:

Linden -+- lib -+- jruby -+- 1.8 -+- bin -+- bundle
| | +- httparty
| | +- oauth
| +- cache -+- ...
| +- doc
| +- gems -+- bouncy-castle-java-1.5.0145.2 -+- ...
| | +- bundler-1.0.3 -+- ...
| | +- crack-0.1.8 -+- ...
| | +- hashie-0.4.0 -+- ...
| | +- httparty-0.6.1 -+- ...
| | +- jruby-openssl-0.7.1 -+- ...
| | +- multi_json-0.0.4 -+- ...
| | +- oauth-0.4.3 -+- ...
| | +- twitter-0.9.12 -+- ...
| +- specifications -+- ...
+- jruby-complete.jar
+- Gemfile
+- Gemfile.lock
+- src -+- linden -+- SearchSample.java
+- build
+- Rakefile


When I typed "rake ant:java", the Java code above worked and printed out one tweet.

java -jar lib/jruby-complete.jar -S rake ant:java
(in /Users/yoko/Works/tmp/Linden)
basepath: /Users/yoko/Works/tmp/Linden
jrubyhome: file:/Users/yoko/Works/tmp/Linden/lib/jruby-complete.jar!/META-INF/jruby.home
{"profile_image_url"=>
"http://a3.twimg.com/profile_images/76084835/web-profile_normal.jpg",
"created_at"=>"Fri, 22 Oct 2010 15:38:09 +0000",
"from_user"=>"mccrory",
"metadata"=>{"result_type"=>"recent"},
"to_user_id"=>nil,
"text"=>
"RT @carlosqt: #IronRuby 1.1.1 a released! download from: http://ironruby.codeplex.com/ #programming #JRuby #Ruby #Rails #dotnet #code",
"id"=>28415816774,
"from_user_id"=>1757577,
"geo"=>nil,
"iso_language_code"=>"en",
"source"=>"<a href="http://twitter.com/">web</a>"}


Packaging


This time I can't rely on GEM_PATH environment variable. If GEM_PATH had worked also for the path in a jar, unfortunately, it didn't. Instead of GEM_PATH, I set all path to gems to ScriptingContainer. This was not complicated since all gems were installed in the same directory. I fixed the jar name, "sample.jar," so this name appeared in the Java code. This name could have been given via a command line argument. Edited SearchSample.java is in below:

package linden;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import org.jruby.embed.LocalContextScope;
import org.jruby.embed.ScriptingContainer;

public class SearchSample {
private String jarname = "sample.jar";

private SearchSample() throws IOException {
String basepath = System.getProperty("user.dir");
System.out.println("basepath: " + basepath);
ScriptingContainer container = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
System.out.println("jrubyhome: " + container.getHomeDirectory());
container.setLoadPaths(getGemPaths(jarname, basepath));

String script =
"require 'rubygems'\n" +
"require 'twitter'\n" +
"require 'pp'\n" +
"pp Twitter::Search.new('#jruby').fetch.results.first";
container.runScriptlet(script);
}

private List<String> getGemPaths(String jarname, String basepath) throws IOException {
JarFile jarFile = new JarFile(basepath + "/" + jarname);
Enumeration<JarEntry> entries = jarFile.entries();
String gempath = "lib/jruby/1.8/gems/";
Set<String> gemnames = new HashSet<String>();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String entryName = entry.getName();
if (entryName.startsWith(gempath) && entryName.length() > gempath.length()) {
String n = entryName.substring(gempath.length());
String m = n.substring(0, n.indexOf("/"));
gemnames.add(m);
}
}
List<String> gemPaths = new ArrayList<String>();
for (String gem : gemnames) {
gemPaths.add("file:" + basepath + "/" + jarname + "!/lib/jruby/1.8/gems/" + gem + "/lib");
}
return gemPaths;
}


public static void main(String[] args) throws IOException {
new SearchSample();
}
}

To make jar archive, I added jar task to my Rakefile.

require 'ant'

namespace :ant do
task :compile => :clean do
ant.javac :srcdir => "src", :destdir => "build"
end
end

namespace :ant do
task :java => :compile do
ant.java :classname => "linden.SearchSample" do
classpath do
pathelement :location => "lib/jruby-complete.jar"
pathelement :path => "build"
end
end
end
end

namespace :ant do
task :jar => :compile do
ant.jar :basedir => ".", :destfile => "sample.jar" do
fileset :dir => "build" do
include :name => "**/*.class"
end
include :name => "lib/jruby/1.8/gems/**/*"
manifest do
attribute :name => "Main-Class", :value => "linden.SearchSample"
end
end
end
end


require 'rake/clean'

CLEAN.include '*.class', '*.jar'

All right, let's create jar archive.

java -jar lib/jruby-complete.jar -S rake ant:jar

The ant:jar task created the sample.jar archive in Linden directory. To test this archive was really gems in a jar, I unset GEM_PATH environment variable first. Then, I typed java command and got one tweet, Yay!

unset GEM_PATH; echo $GEM_PATH
java -cp lib/jruby-complete.jar:sample.jar linden.SearchSample

To ensure that the jar archive had gems in the jar, I moved sample.jar to a different directory and typed java command with the full path to jruby-complete.jar. It worked.

cp sample.jar ../.
cd ..
java -cp Linden/lib/jruby-complete.jar:sample.jar linden.SearchSample



This might be one answer of packaging gems in a jar and using gems from RedBridge.