Ruby in Java, Java in Ruby, JRuby or Ruby Java Bridge?

Hosting (J)Rails applications on a high availability Java infrastructure with clusters, loadbalancers and all that shit stuff is great, if you already have it in place.

But does running an app on the JRE make it a JRuby application by default? Do you really want to be stuck on JRuby?

I really like the idea of running code with the most appropriate runtime available, that is why I use RVM all the time. JRuby is still very slow on startup and tools like autotest/autospec take minutes for just a handfull of tests…

So I would like to host Rails applications on a Tomcat, but on the other hand I want the tests to be executed with MRI! This should not be a problem as long as you don’t want to share a common codebase within Ruby and Java.

Integration

Is there a best practice for combining Java and Ruby?

Since every project is different, I think that you’ve got to evaluate the possible solutions to pick the one that fits best.

The next sections cover some approaches on integration of Java and Ruby code. Make sure to have RVM installed if you want to execute the provided example code. I assume that a JRE is provided with every OS nowadays…

Java in Ruby

There are two common solutions for embeding Java into a Ruby application. The first and obvious one is via JRuby, which can only be run with the JRuby runtime:

require 'java'

puts "access java.util.UUID via JRuby"
puts java.util.UUID.randomUUID().toString()
rvm use jruby 
=> Using jruby 1.5.0.RC1

jruby lib/jruby.rb 
=> access java.util.UUID via JRuby
=> a16fda6a-c57d-43b9-8376-801e48fe56b3

The same thing is possible using the Ruby Java Bridge from MRI:

require 'rjb'

puts "access java.util.UUID via RJB"
puts Rjb::import('java.util.UUID').randomUUID().toString()
rvm use 1.8.7
=> Using ruby 1.8.7 p249

ruby lib/rjb.rb 
=> access java.util.UUID via RJB
=> 1db8298c-5486-4933-be00-cdb180388e38

But it is also possible to do it the other way around!

Ruby in Java

Evaluating Ruby code in Java is dead simple with help of the JRuby library. You just need to set up a scripting container that executes your scripts:

  @Test
  public void execute_jruby_scriptlet() {
    new ScriptingContainer().runScriptlet("puts 'hello jruby world'");
  }

A more advanced example is to wire a Ruby class as a Spring bean. You need to provide some configuration and a Java interface that can be used as the basis for the bean:


<lang:jruby id="identifier" script-interfaces="de.nofail.Identifier" script-source="classpath:/ruby/identifier.rb">
    <lang:property name="uuid" value="#{ T(java.util.UUID).randomUUID().toString() }" />
</lang:jruby>
# identifier.rb
class Identifier
  def setUuid(uuid)
    @uuid = uuid
  end
  def getUuid()
    @uuid
  end
  def to_s()
    "Identifier[#{@uuid}]"
  end
end

# don't forget to return an instance as a bean
Identifier.new
// Identifier.java
public interface Identifier {
  String getUuid();
}
// SpringJRubyBeanTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class SpringJRubyBeanTest {

  @Resource(name = "identifier")
  Identifier identifier;

  @Test
  public void generateUuid_success() {
    System.out.println("uuid: " + identifier);
  }
}

The code is currently not running with JRuby 1.5.0.RC1 so you need the latest stable release for testing.

checking Ruby enginges

It is even possible to mix and match all those approches! You just need to keep track of the Ruby engine that is evaluating your code. I created a little helper called which_ruby (available on rubygems) as a sidekick for this article, but more on that in the next week.

Some Ruby sugar in your cup of Java!

taming webapp logging with log4j

Logging is an aspect of programming that should follow some simple rules. If I should describe logging for a dictionary it would be something like: “Providing essential information with object introspection at a decent level of output that enables you to look into a running application or providing runtime information for debugging.”

But even the simplest thing can be complicated if you use Java…

There are different logging frameworks like log4j, commons logging or logback even though Java includes it’s own java.util.logging API. To make it even worse, the Java community came up with a simple logging facade so one can use the logging framework of choice but implement against the facade - WTF?!

a standard that sucks

I would guess that 99% of Java code is written against log4j. Nobody really want’s to switch a logging framework during implementation and most developers I know see log4j as a technical standard.

I won’t recommend using log4j to anyone. The log4j documentation is crap! There is no usefull literature either (I never thought that one would have to buy a book for logging…). The framework misses essential things like pattern matching filters (you will have to use log4j companions) and it is difficult to configure, especially on a tomcat.

Log4j is a bitch when it comes to deployment. One has to be careful where to put the log4j jar(s) and xml or plain text configuration files. If you do it wrong you end up with broken logging behavior; webapps log to the wrong log file, spit out stuff to the system out or stop logging at all.

this is how we do it

Our Java infrastructure is service oriented. We have a bunch of webservices hosted on serveral distributed tomcats and all wars depend on log4j for logging. We want to reload the logging configuration files at runtime (why is jboss able to do it and tomcat is not?!) and manipulate it via JMX.

additional log4j configuration

To achieve this, we have to put some additional configuration into every webapp. Log4j comes with a watchdog that looks for changes in the configuration files. The functionality can be enabled by using the configureAndWatch() method. There is a precanned Spring solution for this, but it has configuration overhead and environmental restrictions. So the best place to implement it ourselves is in a custom context listener:


   [...]
	
	<listener>
		<listener-class>de.nofail.TomcatLoggingListener</listener-class>
	</listener>
   [...]
   [...]
	public void contextInitialized(ServletContextEvent sce) {
		ServletContext servletContext = sce.getServletContext();
		try {
			String log4jFile = String.format("%s-log4j.xml", servletContext.getContextPath());
			String configFilename = new File(getClass().getResource(log4jFile).toURI()).getAbsolutePath();
			DOMConfigurator.configureAndWatch(configFilename, 1000);
		} catch (URISyntaxException e) {
			throw new IllegalStateException("Error resolving log4j configuration file for context=" + servletContext, e);
		}
	}
   [...]

This listener will use a config file named WEBAPP-log4j.xml lying in the root directory. The goal is, that every webapp has it’s own logging context. Every war should include a log4j.jar so that logging won’t be affected by other apps, the classloader hierarchie will ensure this. The different xml configuration files can than be placed in a shared tomcat directory for easy access of an operations team (which is probably yourself).

Using different configuration files has the advantage that you don’t have to mess around with appenders based on packages. That’s how one can configure different log levels for the same packages in each application, very usefull if you release a new application!

Java Management Extension

There are different ways to integrate JMX functionality into a webapp. The simplest approach is to use another servlet listener for propagating an mbean:


   [...]
	
	<listener>
		<listener-class>de.nofail.JMXListener</listener-class>
	</listener>
   [...]
   [...]
	public void contextInitialized(final ServletContextEvent sce) {
		try {
			TomcatLogging mbean = new TomcatLogging();
			ObjectName clutter = new ObjectName("de.nofail:type=TomcatLogging");
			ManagementFactory.getPlatformMBeanServer().registerMBean(mbean, clutter);
		} catch (Exception e) {
			throw new IllegalStateException("Could not create JMX context: " + e.getMessage(), e);
		}
	}
   [...]

If you are using Spring it’s easy to add JMX support via annotations.

Now you can access your running webapp via jconsole:

more information?

You can have a look at a working example based on Maven on github.

Drink a cup of Java™©®, but don’t forget the sugar!