JRuby Support
Aquarium runs under JRuby. The rake website
target runs all the specs under JRuby to ensure that they work as expected.
In addition, JRuby can now advise Java types (with some limitations…)!
Advising Java Types in JRuby
While you might normally turn to AspectJ or the Spring Framework to advise Java classes, if you are using JRuby, you can now use Aquarium! The advantages and disadvantages of Aquarium compared to AspectJ are listed in the README. Briefly, Aquarium lets you add and remove advice dynamically at runtime, you can advise JDK types easily, and Aquarium lets you advise individual objects. The disadvantages of Aquarium are that it will be slower than using AspectJ, Aquarium’s pointcut language is not as full-featured as AspectJ’s, and there are some bugs and limitations in this initial support (V0.4.0).
Here is an example of adding tracing calls to a method doIt
in all classes that implement the Java interface com.foo.Work
.
There are two important points to notice in this example:
- You can choose to refer to the method as
do_it
(Ruby style) ordoIt
, but these variants are effectively treated as separate methods; advice on one will not affect invocations of the other. So, if you want to be sure to catch all invocations, use both forms. There is a bug (18326) that happens in certain conditions if you use just the Java naming convention. - If the type is an interface, you must use
:types_and_descendents
(or one of the variants on the wordtypes
). Since interfaces don’t have method implementations, you will match no join points unless you use theand_descendents
clause. (By default, Aquarium warns you when no join points are matched by an aspect.) However, there is a bug (18325) with this approach if Java types are subtyped in Ruby.
Limitations and Bugs
(Here’s the “fine print”…) In this (V0.4.0) release, there are some important limitations and a few bugs (either in Aquarium or JRuby??).
- Aquarium advice on a method in a Java type will only be invoked when the method is called directly from Ruby.
- To have the advice invoked when the method is called from either Java or Ruby, it is necessary to create a subclass of the Java type in Ruby and an override of the method, which can just call “super”. Note that it will be necessary for instances of this Ruby type to be used throughout, not instances of a Java parent type.
- BUG #18325: If you have Ruby subclasses of Java types and you advise a Java method in the hierarchy using
:types_and_descendents => MyJavaBaseClassOrInterface
and you call unadvise on the aspect, the advice “infrastructure” is not correctly removed from the Ruby types. Workaround: Only advise methods in Ruby subclasses of Java types where the method is explicitly overridden in the Ruby class. (The spec provides examples.) - BUG #18326: Normally, you can use either Java- or Ruby-style method names (e.g.,
doSomething
vs.do_something
), for Java types. However, if you write an aspect using the Java-style for a method name and a Ruby subclass of the Java type where the method is actually defined (i.e., the Ruby class doesn’t override the method), it appears that the JoinPoint was advised, but the advice is never called. Workaround: Use the Ruby-style name in this scenario.
For more details, see the RSpec examples in the jruby/spec
directory.